summaryrefslogtreecommitdiff
path: root/libpurple
diff options
context:
space:
mode:
Diffstat (limited to 'libpurple')
-rw-r--r--libpurple/Makefile.am22
-rw-r--r--libpurple/account.c161
-rw-r--r--libpurple/account.h61
-rw-r--r--libpurple/certificate.c6
-rw-r--r--libpurple/connection.c9
-rw-r--r--libpurple/conversation.c4
-rw-r--r--libpurple/dbus-analyze-functions.py2
-rw-r--r--libpurple/debug.h12
-rw-r--r--libpurple/desktopitem.c2
-rw-r--r--libpurple/ft.c120
-rw-r--r--libpurple/ft.h61
-rw-r--r--libpurple/gconf/Makefile.am6
-rw-r--r--libpurple/log.c3
-rw-r--r--libpurple/media.c3
-rw-r--r--libpurple/media/backend-fs2.c72
-rw-r--r--libpurple/media/backend-fs2.h2
-rw-r--r--libpurple/media/codec.c10
-rw-r--r--libpurple/media/codec.h3
-rw-r--r--libpurple/network.c58
-rw-r--r--libpurple/network.h75
-rw-r--r--libpurple/plugins/Makefile.mingw2
-rw-r--r--libpurple/plugins/idle.c3
-rw-r--r--libpurple/plugins/perl/common/Log.xs3
-rw-r--r--libpurple/plugins/signals-test.c4
-rw-r--r--libpurple/plugins/tcl/tcl_cmd.c9
-rw-r--r--libpurple/plugins/tcl/tcl_signals.c6
-rw-r--r--libpurple/prefs.c1
-rw-r--r--libpurple/protocols/bonjour/bonjour.c15
-rw-r--r--libpurple/protocols/bonjour/jabber.c11
-rw-r--r--libpurple/protocols/bonjour/mdns_avahi.c3
-rw-r--r--libpurple/protocols/gg/Makefile.am2
-rw-r--r--libpurple/protocols/gg/gg.c33
-rw-r--r--libpurple/protocols/gg/lib/common.c2
-rw-r--r--libpurple/protocols/gg/lib/events.c2
-rw-r--r--libpurple/protocols/gg/lib/libgadu-internal.h1
-rw-r--r--libpurple/protocols/gg/lib/libgadu.c5
-rw-r--r--libpurple/protocols/gg/lib/libgadu.h4
-rw-r--r--libpurple/protocols/gg/lib/resolver.c4
-rw-r--r--libpurple/protocols/gg/search.c2
-rw-r--r--libpurple/protocols/irc/cmds.c8
-rw-r--r--libpurple/protocols/irc/irc.c4
-rw-r--r--libpurple/protocols/irc/msgs.c2
-rw-r--r--libpurple/protocols/jabber/auth.c50
-rw-r--r--libpurple/protocols/jabber/auth.h1
-rw-r--r--libpurple/protocols/jabber/auth_cyrus.c102
-rw-r--r--libpurple/protocols/jabber/auth_digest_md5.c18
-rw-r--r--libpurple/protocols/jabber/auth_plain.c11
-rw-r--r--libpurple/protocols/jabber/auth_scram.c58
-rw-r--r--libpurple/protocols/jabber/bosh.c53
-rw-r--r--libpurple/protocols/jabber/bosh.h1
-rw-r--r--libpurple/protocols/jabber/caps.c119
-rw-r--r--libpurple/protocols/jabber/caps.h11
-rw-r--r--libpurple/protocols/jabber/chat.c15
-rw-r--r--libpurple/protocols/jabber/chat.h2
-rw-r--r--libpurple/protocols/jabber/data.c103
-rw-r--r--libpurple/protocols/jabber/data.h12
-rw-r--r--libpurple/protocols/jabber/disco.c2
-rw-r--r--libpurple/protocols/jabber/ibb.c5
-rw-r--r--libpurple/protocols/jabber/ibb.h5
-rw-r--r--libpurple/protocols/jabber/jabber.c264
-rw-r--r--libpurple/protocols/jabber/jabber.h25
-rw-r--r--libpurple/protocols/jabber/jingle/transport.c4
-rw-r--r--libpurple/protocols/jabber/jutil.c4
-rw-r--r--libpurple/protocols/jabber/libxmpp.c28
-rw-r--r--libpurple/protocols/jabber/message.c52
-rw-r--r--libpurple/protocols/jabber/namespaces.h6
-rw-r--r--libpurple/protocols/jabber/oob.c4
-rw-r--r--libpurple/protocols/jabber/parser.c17
-rw-r--r--libpurple/protocols/jabber/presence.c6
-rw-r--r--libpurple/protocols/jabber/roster.c3
-rw-r--r--libpurple/protocols/jabber/si.c114
-rw-r--r--libpurple/protocols/jabber/xdata.c26
-rw-r--r--libpurple/protocols/jabber/xdata.h15
-rw-r--r--libpurple/protocols/msn/Makefile.am2
-rw-r--r--libpurple/protocols/msn/Makefile.mingw1
-rw-r--r--libpurple/protocols/msn/contact.c25
-rw-r--r--libpurple/protocols/msn/directconn.c1157
-rw-r--r--libpurple/protocols/msn/directconn.h172
-rw-r--r--libpurple/protocols/msn/httpconn.c10
-rw-r--r--libpurple/protocols/msn/msg.c162
-rw-r--r--libpurple/protocols/msn/msn.c354
-rw-r--r--libpurple/protocols/msn/msn.h5
-rw-r--r--libpurple/protocols/msn/msnutils.c2
-rw-r--r--libpurple/protocols/msn/notification.c88
-rw-r--r--libpurple/protocols/msn/notification.h4
-rw-r--r--libpurple/protocols/msn/object.c56
-rw-r--r--libpurple/protocols/msn/object.h34
-rw-r--r--libpurple/protocols/msn/session.c37
-rw-r--r--libpurple/protocols/msn/session.h16
-rw-r--r--libpurple/protocols/msn/slp.c694
-rw-r--r--libpurple/protocols/msn/slp.h8
-rw-r--r--libpurple/protocols/msn/slpcall.h2
-rw-r--r--libpurple/protocols/msn/slplink.c230
-rw-r--r--libpurple/protocols/msn/slplink.h11
-rw-r--r--libpurple/protocols/msn/slpmsg.c4
-rw-r--r--libpurple/protocols/msn/soap.c6
-rw-r--r--libpurple/protocols/msn/state.c4
-rw-r--r--libpurple/protocols/msn/switchboard.c28
-rw-r--r--libpurple/protocols/msn/user.h4
-rw-r--r--libpurple/protocols/mxit/actions.c137
-rw-r--r--libpurple/protocols/mxit/chunk.h2
-rw-r--r--libpurple/protocols/mxit/formcmds.c54
-rw-r--r--libpurple/protocols/mxit/http.c6
-rw-r--r--libpurple/protocols/mxit/login.c29
-rw-r--r--libpurple/protocols/mxit/markup.c25
-rw-r--r--libpurple/protocols/mxit/multimx.c71
-rw-r--r--libpurple/protocols/mxit/multimx.h1
-rw-r--r--libpurple/protocols/mxit/mxit.c71
-rw-r--r--libpurple/protocols/mxit/mxit.h6
-rw-r--r--libpurple/protocols/mxit/profile.c35
-rw-r--r--libpurple/protocols/mxit/profile.h3
-rw-r--r--libpurple/protocols/mxit/protocol.c193
-rw-r--r--libpurple/protocols/mxit/protocol.h26
-rw-r--r--libpurple/protocols/mxit/roster.c152
-rw-r--r--libpurple/protocols/mxit/roster.h12
-rw-r--r--libpurple/protocols/myspace/myspace.c4
-rw-r--r--libpurple/protocols/novell/novell.c4
-rw-r--r--libpurple/protocols/null/nullprpl.c8
-rw-r--r--libpurple/protocols/oscar/Makefile.am10
-rw-r--r--libpurple/protocols/oscar/Makefile.mingw16
-rw-r--r--libpurple/protocols/oscar/authorization.c153
-rw-r--r--libpurple/protocols/oscar/bstream.c127
-rw-r--r--libpurple/protocols/oscar/clientlogin.c11
-rw-r--r--libpurple/protocols/oscar/encoding.c235
-rw-r--r--libpurple/protocols/oscar/encoding.h46
-rw-r--r--libpurple/protocols/oscar/family_admin.c10
-rw-r--r--libpurple/protocols/oscar/family_advert.c51
-rw-r--r--libpurple/protocols/oscar/family_alert.c4
-rw-r--r--libpurple/protocols/oscar/family_auth.c34
-rw-r--r--libpurple/protocols/oscar/family_bart.c4
-rw-r--r--libpurple/protocols/oscar/family_bos.c92
-rw-r--r--libpurple/protocols/oscar/family_buddy.c111
-rw-r--r--libpurple/protocols/oscar/family_chat.c218
-rw-r--r--libpurple/protocols/oscar/family_chatnav.c82
-rw-r--r--libpurple/protocols/oscar/family_feedbag.c208
-rw-r--r--libpurple/protocols/oscar/family_icbm.c1621
-rw-r--r--libpurple/protocols/oscar/family_icq.c403
-rw-r--r--libpurple/protocols/oscar/family_invite.c53
-rw-r--r--libpurple/protocols/oscar/family_locate.c270
-rw-r--r--libpurple/protocols/oscar/family_odir.c264
-rw-r--r--libpurple/protocols/oscar/family_oservice.c212
-rw-r--r--libpurple/protocols/oscar/family_userlookup.c2
-rw-r--r--libpurple/protocols/oscar/flap_connection.c28
-rw-r--r--libpurple/protocols/oscar/libaim.c9
-rw-r--r--libpurple/protocols/oscar/libicq.c10
-rw-r--r--libpurple/protocols/oscar/misc.c36
-rw-r--r--libpurple/protocols/oscar/msgcookie.c15
-rw-r--r--libpurple/protocols/oscar/odc.c11
-rw-r--r--libpurple/protocols/oscar/oft.c2
-rw-r--r--libpurple/protocols/oscar/oscar.c2229
-rw-r--r--libpurple/protocols/oscar/oscar.h583
-rw-r--r--libpurple/protocols/oscar/oscar_data.c4
-rw-r--r--libpurple/protocols/oscar/oscarcommon.h1
-rw-r--r--libpurple/protocols/oscar/peer_proxy.c10
-rw-r--r--libpurple/protocols/oscar/rxhandlers.c191
-rw-r--r--libpurple/protocols/oscar/snac.c16
-rw-r--r--libpurple/protocols/oscar/tlv.c52
-rw-r--r--libpurple/protocols/oscar/userinfo.c553
-rw-r--r--libpurple/protocols/oscar/util.c205
-rw-r--r--libpurple/protocols/oscar/visibility.c199
-rw-r--r--libpurple/protocols/oscar/visibility.h (renamed from libpurple/protocols/oscar/family_translate.c)30
-rw-r--r--libpurple/protocols/qq/ChangeLog2
-rw-r--r--libpurple/protocols/qq/buddy_info.c11
-rw-r--r--libpurple/protocols/qq/buddy_list.c16
-rw-r--r--libpurple/protocols/qq/buddy_opt.c16
-rw-r--r--libpurple/protocols/qq/char_conv.c20
-rw-r--r--libpurple/protocols/qq/file_trans.c6
-rw-r--r--libpurple/protocols/qq/group.c2
-rw-r--r--libpurple/protocols/qq/group_im.c30
-rw-r--r--libpurple/protocols/qq/group_join.c6
-rw-r--r--libpurple/protocols/qq/group_opt.c2
-rw-r--r--libpurple/protocols/qq/im.c13
-rw-r--r--libpurple/protocols/qq/qq.c16
-rw-r--r--libpurple/protocols/qq/qq_base.c18
-rw-r--r--libpurple/protocols/qq/qq_network.c6
-rw-r--r--libpurple/protocols/qq/qq_process.c22
-rw-r--r--libpurple/protocols/qq/qq_trans.c9
-rw-r--r--libpurple/protocols/qq/send_file.c10
-rw-r--r--libpurple/protocols/sametime/sametime.c2
-rw-r--r--libpurple/protocols/silc/chat.c4
-rw-r--r--libpurple/protocols/silc/ops.c2
-rw-r--r--libpurple/protocols/silc/silc.c4
-rw-r--r--libpurple/protocols/silc/silcpurple.h2
-rw-r--r--libpurple/protocols/silc10/chat.c4
-rw-r--r--libpurple/protocols/silc10/ops.c2
-rw-r--r--libpurple/protocols/silc10/silc.c5
-rw-r--r--libpurple/protocols/silc10/silcpurple.h2
-rw-r--r--libpurple/protocols/simple/simple.c125
-rw-r--r--libpurple/protocols/simple/sipmsg.c16
-rw-r--r--libpurple/protocols/yahoo/libyahoo.c6
-rw-r--r--libpurple/protocols/yahoo/libyahoojp.c6
-rw-r--r--libpurple/protocols/yahoo/libymsg.c132
-rw-r--r--libpurple/protocols/yahoo/libymsg.h1
-rw-r--r--libpurple/protocols/yahoo/util.c17
-rw-r--r--libpurple/protocols/yahoo/yahoo_aliases.c2
-rw-r--r--libpurple/protocols/yahoo/yahoo_doodle.c4
-rw-r--r--libpurple/protocols/yahoo/yahoo_doodle.h2
-rw-r--r--libpurple/protocols/yahoo/yahoo_filexfer.c28
-rw-r--r--libpurple/protocols/yahoo/yahoochat.c12
-rw-r--r--libpurple/protocols/zephyr/ZVariables.c6
-rw-r--r--libpurple/protocols/zephyr/zephyr.c4
-rw-r--r--libpurple/proxy.c15
-rw-r--r--libpurple/prpl.h47
-rwxr-xr-xlibpurple/purple-url-handler7
-rw-r--r--libpurple/request.c63
-rw-r--r--libpurple/request.h46
-rw-r--r--libpurple/roomlist.h2
-rw-r--r--libpurple/stun.c24
-rw-r--r--libpurple/tests/Makefile.am1
-rw-r--r--libpurple/tests/check_libpurple.c17
-rw-r--r--libpurple/tests/test_jabber_caps.c54
-rw-r--r--libpurple/tests/test_jabber_scram.c2
-rw-r--r--libpurple/tests/test_util.c106
-rw-r--r--libpurple/tests/tests.h1
-rw-r--r--libpurple/upnp.c27
-rw-r--r--libpurple/util.c471
-rw-r--r--libpurple/util.h23
-rw-r--r--libpurple/win32/global.mak3
-rw-r--r--libpurple/win32/rules.mak3
-rw-r--r--libpurple/win32/targets.mak3
-rw-r--r--libpurple/win32/win32dep.c17
-rw-r--r--libpurple/xmlnode.c6
-rw-r--r--libpurple/xmlnode.h4
223 files changed, 7609 insertions, 8256 deletions
diff --git a/libpurple/Makefile.am b/libpurple/Makefile.am
index ae73559e38..7fbea9504f 100644
--- a/libpurple/Makefile.am
+++ b/libpurple/Makefile.am
@@ -170,13 +170,11 @@ purple_mediaheaders = \
purple_builtheaders = purple.h version.h marshallers.h
marshallers.h: marshallers.list
- @echo "Generating marshallers.h"
- $(GLIB_GENMARSHAL) --prefix=purple_smarshal $(srcdir)/marshallers.list --header > marshallers.h
+ $(AM_V_GEN)$(GLIB_GENMARSHAL) --prefix=purple_smarshal $(srcdir)/marshallers.list --header > marshallers.h
marshallers.c: marshallers.list marshallers.h
- @echo "Generating marshallers.c"
- echo "#include \"marshallers.h\"" > marshallers.c
- $(GLIB_GENMARSHAL) --prefix=purple_smarshal $(srcdir)/marshallers.list --body >> marshallers.c
+ $(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
@@ -212,16 +210,16 @@ dbus_signals = $(addprefix $(srcdir)/, $(purple_coresources)) \
$(srcdir)/protocols/jabber/libxmpp.c
dbus-types.c: dbus-analyze-types.py $(purple_build_coreheaders)
- cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --pattern=PURPLE_DBUS_DEFINE_TYPE\(%s\) > $@
+ $(AM_V_GEN)cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --pattern=PURPLE_DBUS_DEFINE_TYPE\(%s\) > $@
dbus-types.h: dbus-analyze-types.py $(purple_build_coreheaders)
- cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --pattern=PURPLE_DBUS_DECLARE_TYPE\(%s\) > $@
+ $(AM_V_GEN)cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --pattern=PURPLE_DBUS_DECLARE_TYPE\(%s\) > $@
dbus-bindings.c: dbus-analyze-functions.py $(dbus_exported)
- cat $(dbus_build_exported) | $(PYTHON) $(srcdir)/dbus-analyze-functions.py > $@
+ $(AM_V_GEN)cat $(dbus_build_exported) | $(PYTHON) $(srcdir)/dbus-analyze-functions.py > $@
dbus-signals.c: dbus-analyze-signals.py $(dbus_signals)
- cat $(dbus_signals) | $(PYTHON) $(srcdir)/dbus-analyze-signals.py > $@
+ $(AM_V_GEN)cat $(dbus_signals) | $(PYTHON) $(srcdir)/dbus-analyze-signals.py > $@
dbus-server.$(OBJEXT): dbus-bindings.c dbus-signals.c dbus-types.c dbus-types.h
dbus-server.lo: dbus-bindings.c dbus-signals.c dbus-types.c dbus-types.h
@@ -236,11 +234,11 @@ libpurple_client_la_LDFLAGS = -version-info $(PURPLE_LT_VERSION_INFO) -no-undefi
libpurple_client_la_LIBADD = $(DBUS_LIBS)
purple-client-bindings.c: dbus-analyze-functions.py $(dbus_exported)
- cat $(dbus_build_exported) | $(PYTHON) $(srcdir)/dbus-analyze-functions.py --client > $@
+ $(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)
- cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --keyword=enum --verbatim > $@
- cat $(dbus_build_exported) | $(PYTHON) $(srcdir)/dbus-analyze-functions.py --client --headers >> $@
+ $(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 >> $@
$(libpurple_client_la_OBJECTS): purple-client-bindings.h purple-client-bindings.c
diff --git a/libpurple/account.c b/libpurple/account.c
index e7b198fba5..4d500dec94 100644
--- a/libpurple/account.c
+++ b/libpurple/account.c
@@ -513,6 +513,25 @@ migrate_yahoo_japan(PurpleAccount *account)
}
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;
@@ -579,6 +598,9 @@ parse_settings(xmlnode *node, PurpleAccount *account)
/* 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 to do it before the user views the
+ * Edit Account dialog. */
+ migrate_xmpp_encryption(account);
}
static GList *
@@ -1129,7 +1151,7 @@ request_password_ok_cb(PurpleAccount *account, PurpleRequestFields *fields)
static void
request_password_cancel_cb(PurpleAccount *account, PurpleRequestFields *fields)
{
- /* Disable the account as the user has canceled connecting */
+ /* Disable the account as the user has cancelled connecting */
purple_account_set_enabled(account, purple_core_get_ui(), FALSE);
}
@@ -1709,6 +1731,14 @@ purple_account_set_proxy_info(PurpleAccount *account, PurpleProxyInfo *info)
}
void
+purple_account_set_privacy_type(PurpleAccount *account, PurplePrivacyType privacy_type)
+{
+ g_return_if_fail(account != NULL);
+
+ account->perm_deny = privacy_type;
+}
+
+void
purple_account_set_status_types(PurpleAccount *account, GList *status_types)
{
g_return_if_fail(account != NULL);
@@ -1774,6 +1804,92 @@ purple_account_set_status_list(PurpleAccount *account, const char *status_id,
schedule_accounts_save();
}
+struct public_alias_closure
+{
+ PurpleAccount *account;
+ gpointer failure_cb;
+};
+
+static gboolean
+set_public_alias_unsupported(gpointer data)
+{
+ struct public_alias_closure *closure = data;
+ PurpleSetPublicAliasFailureCallback failure_cb = closure->failure_cb;
+
+ failure_cb(closure->account,
+ _("This protocol does not support setting a public alias."));
+ g_free(closure);
+
+ return FALSE;
+}
+
+void
+purple_account_set_public_alias(PurpleAccount *account,
+ const char *alias, PurpleSetPublicAliasSuccessCallback success_cb,
+ PurpleSetPublicAliasFailureCallback failure_cb)
+{
+ PurpleConnection *gc;
+ PurplePlugin *prpl = NULL;
+ PurplePluginProtocolInfo *prpl_info = NULL;
+
+ g_return_if_fail(account != NULL);
+ g_return_if_fail(purple_account_is_connected(account));
+
+ gc = purple_account_get_connection(account);
+ prpl = purple_connection_get_prpl(gc);
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+
+ if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, set_public_alias))
+ prpl_info->set_public_alias(gc, alias, success_cb, failure_cb);
+ else if (failure_cb) {
+ struct public_alias_closure *closure =
+ g_new0(struct public_alias_closure, 1);
+ closure->account = account;
+ closure->failure_cb = failure_cb;
+ purple_timeout_add(0, set_public_alias_unsupported, closure);
+ }
+}
+
+static gboolean
+get_public_alias_unsupported(gpointer data)
+{
+ struct public_alias_closure *closure = data;
+ PurpleGetPublicAliasFailureCallback failure_cb = closure->failure_cb;
+
+ failure_cb(closure->account,
+ _("This protocol does not support fetching the public alias."));
+ g_free(closure);
+
+ return FALSE;
+}
+
+void
+purple_account_get_public_alias(PurpleAccount *account,
+ PurpleGetPublicAliasSuccessCallback success_cb,
+ PurpleGetPublicAliasFailureCallback failure_cb)
+{
+ PurpleConnection *gc;
+ PurplePlugin *prpl = NULL;
+ PurplePluginProtocolInfo *prpl_info = NULL;
+
+ g_return_if_fail(account != NULL);
+ g_return_if_fail(purple_account_is_connected(account));
+
+ gc = purple_account_get_connection(account);
+ prpl = purple_connection_get_prpl(gc);
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+
+ if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_public_alias))
+ prpl_info->get_public_alias(gc, success_cb, failure_cb);
+ else if (failure_cb) {
+ struct public_alias_closure *closure =
+ g_new0(struct public_alias_closure, 1);
+ closure->account = account;
+ closure->failure_cb = failure_cb;
+ purple_timeout_add(0, get_public_alias_unsupported, closure);
+ }
+}
+
void
purple_account_clear_settings(PurpleAccount *account)
{
@@ -2105,6 +2221,14 @@ purple_account_get_proxy_info(const PurpleAccount *account)
return account->proxy_info;
}
+PurplePrivacyType
+purple_account_get_privacy_type(const PurpleAccount *account)
+{
+ g_return_val_if_fail(account != NULL, PURPLE_PRIVACY_ALLOW_ALL);
+
+ return account->perm_deny;
+}
+
PurpleStatus *
purple_account_get_active_status(const PurpleAccount *account)
{
@@ -2510,6 +2634,19 @@ signed_on_cb(PurpleConnection *gc,
{
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
@@ -2560,6 +2697,9 @@ connection_error_cb(PurpleConnection *gc,
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 *
@@ -2901,8 +3041,27 @@ purple_accounts_init(void)
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);
diff --git a/libpurple/account.h b/libpurple/account.h
index 192b239010..9f958a2e5e 100644
--- a/libpurple/account.h
+++ b/libpurple/account.h
@@ -39,6 +39,10 @@ typedef gboolean (*PurpleFilterAccountFunc)(PurpleAccount *account);
typedef void (*PurpleAccountRequestAuthorizationCb)(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);
+typedef void (*PurpleSetPublicAliasFailureCallback)(PurpleAccount *account, const char *error);
+typedef void (*PurpleGetPublicAliasSuccessCallback)(PurpleAccount *account, const char *alias);
+typedef void (*PurpleGetPublicAliasFailureCallback)(PurpleAccount *account, const char *error);
#include "connection.h"
#include "log.h"
@@ -414,6 +418,16 @@ void purple_account_set_enabled(PurpleAccount *account, const char *ui,
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.
+ *
+ * @since 2.7.0
+ */
+void purple_account_set_privacy_type(PurpleAccount *account, PurplePrivacyType privacy_type);
+
+/**
* Sets the account's status types.
*
* @param account The account.
@@ -452,6 +466,42 @@ void purple_account_set_status_list(PurpleAccount *account,
const char *status_id, gboolean active, GList *attrs);
/**
+ * 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);
+
+/**
+ * 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);
+
+/**
* Clears all protocol-specific settings on an account.
*
* @param account The account.
@@ -683,6 +733,17 @@ gboolean purple_account_get_enabled(const PurpleAccount *account,
PurpleProxyInfo *purple_account_get_proxy_info(const PurpleAccount *account);
/**
+ * Returns the account's privacy type.
+ *
+ * @param account The account.
+ *
+ * @return The privacy type.
+ *
+ * @since 2.7.0
+ */
+PurplePrivacyType purple_account_get_privacy_type(const PurpleAccount *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
diff --git a/libpurple/certificate.c b/libpurple/certificate.c
index f331c7ee15..ff6337bd93 100644
--- a/libpurple/certificate.c
+++ b/libpurple/certificate.c
@@ -99,7 +99,8 @@ invalidity_reason_to_string(PurpleCertificateInvalidityFlags flag)
"that can verify it is currently trusted.");
break;
case PURPLE_CERTIFICATE_NOT_ACTIVATED:
- return _("The certificate is not valid yet.");
+ return _("The certificate is not valid yet. Check that your "
+ "computer's date and time are accurate.");
break;
case PURPLE_CERTIFICATE_EXPIRED:
return _("The certificate has expired and should not be "
@@ -714,6 +715,7 @@ x509_singleuse_start_verify (PurpleCertificateVerificationRequest *vrq)
x509_singleuse_verify_cb );
/* Cleanup */
+ g_free(cn);
g_free(primary);
g_free(secondary);
g_free(sha_asc);
@@ -859,6 +861,7 @@ x509_ca_lazy_init(void)
purple_debug_info("certificate/x509/ca",
"Loaded %s from %s\n",
name ? name : "(unknown)", fullpath);
+ g_free(name);
} else {
purple_debug_error("certificate/x509/ca",
"Failed to load certificate from %s\n",
@@ -1535,6 +1538,7 @@ x509_tls_cached_check_subject_name(PurpleCertificateVerificationRequest *vrq,
"Name mismatch: Certificate given for %s "
"has a name of %s\n",
vrq->subject_name, sn);
+ g_free(sn);
}
x509_tls_cached_complete(vrq, flags);
diff --git a/libpurple/connection.c b/libpurple/connection.c
index 3287e6876b..5f7e52c240 100644
--- a/libpurple/connection.c
+++ b/libpurple/connection.c
@@ -372,6 +372,7 @@ purple_connection_set_state(PurpleConnection *gc, PurpleConnectionState state)
purple_blist_add_account(account);
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);
@@ -515,7 +516,8 @@ purple_connection_disconnect_cb(gpointer data)
account = data;
gc = purple_account_get_connection(account);
- gc->disconnect_timeout = 0;
+ if (gc != NULL)
+ gc->disconnect_timeout = 0;
password = g_strdup(purple_account_get_password(account));
purple_account_disconnect(account);
@@ -715,6 +717,11 @@ purple_connections_init(void)
purple_value_new(PURPLE_TYPE_ENUM),
purple_value_new(PURPLE_TYPE_STRING));
+ purple_signal_register(handle, "autojoin",
+ purple_marshal_BOOLEAN__POINTER, NULL, 1,
+ purple_value_new(PURPLE_TYPE_SUBTYPE,
+ PURPLE_SUBTYPE_CONNECTION));
+
}
void
diff --git a/libpurple/conversation.c b/libpurple/conversation.c
index 3292abb9e4..291b9e156e 100644
--- a/libpurple/conversation.c
+++ b/libpurple/conversation.c
@@ -1124,7 +1124,6 @@ void
purple_conv_im_start_typing_timeout(PurpleConvIm *im, int timeout)
{
PurpleConversation *conv;
- const char *name;
g_return_if_fail(im != NULL);
@@ -1132,7 +1131,6 @@ purple_conv_im_start_typing_timeout(PurpleConvIm *im, int timeout)
purple_conv_im_stop_typing_timeout(im);
conv = purple_conv_im_get_conversation(im);
- name = purple_conversation_get_name(conv);
im->typing_timeout = purple_timeout_add_seconds(timeout, reset_typing_cb, conv);
}
@@ -1520,7 +1518,6 @@ purple_conv_chat_write(PurpleConvChat *chat, const char *who, const char *messag
PurpleAccount *account;
PurpleConversation *conv;
PurpleConnection *gc;
- PurplePluginProtocolInfo *prpl_info;
g_return_if_fail(chat != NULL);
g_return_if_fail(who != NULL);
@@ -1529,7 +1526,6 @@ purple_conv_chat_write(PurpleConvChat *chat, const char *who, const char *messag
conv = purple_conv_chat_get_conversation(chat);
gc = purple_conversation_get_gc(conv);
account = purple_connection_get_account(gc);
- prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
/* Don't display this if the person who wrote it is ignored. */
if (purple_conv_chat_is_user_ignored(chat, who))
diff --git a/libpurple/dbus-analyze-functions.py b/libpurple/dbus-analyze-functions.py
index a52a88dfb0..1a665e0459 100644
--- a/libpurple/dbus-analyze-functions.py
+++ b/libpurple/dbus-analyze-functions.py
@@ -170,7 +170,7 @@ class Binding:
return self.inputpurplestructure(type, name)
# special case for *_get_data functions, be careful here...
- elif (type[0] == "size_t") and (name == "len"):
+ elif (type[0] == "size_t" or type[0] == "gsize") and name == "len":
return self.inputgetdata(type, name)
# unknown pointers are always replaced with NULL
diff --git a/libpurple/debug.h b/libpurple/debug.h
index 67dd91ce7b..b90b86b99c 100644
--- a/libpurple/debug.h
+++ b/libpurple/debug.h
@@ -176,20 +176,24 @@ void purple_debug_set_verbose(gboolean verbose);
gboolean purple_debug_is_verbose(void);
/**
- * Enable or disable verbose debugging. This ordinarily should only be called
+ * 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 verbose debugging or FALSE to disable it.
+ * @param 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
*/
void purple_debug_set_unsafe(gboolean unsafe);
/**
- * Check if unsafe debugging is enabled.
+ * Check if unsafe debugging is enabled. Defaults to FALSE.
*
- * @return TRUE if verbose debugging is enabled, FALSE if it is not.
+ * @return 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
*/
diff --git a/libpurple/desktopitem.c b/libpurple/desktopitem.c
index df0a5b5920..d7f6d39d2d 100644
--- a/libpurple/desktopitem.c
+++ b/libpurple/desktopitem.c
@@ -330,7 +330,7 @@ my_fgets (char *buf, gsize bufsize, FILE *df)
if (c == EOF && pos == 0)
return NULL;
- buf[pos++] = '\0';
+ buf[pos] = '\0';
return buf;
}
diff --git a/libpurple/ft.c b/libpurple/ft.c
index a62a77e6d9..d5711b28eb 100644
--- a/libpurple/ft.c
+++ b/libpurple/ft.c
@@ -60,6 +60,10 @@ typedef struct _PurpleXferPrivData {
/* 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);
@@ -72,6 +76,10 @@ purple_xfer_priv_data_destroy(gpointer data)
if (priv->buffer)
g_byte_array_free(priv->buffer, TRUE);
+ g_free(priv->thumbnail_data);
+
+ g_free(priv->thumbnail_mimetype);
+
g_free(priv);
}
@@ -266,15 +274,21 @@ purple_xfer_set_status(PurpleXfer *xfer, PurpleXferStatusType status)
}
}
-void purple_xfer_conversation_write(PurpleXfer *xfer, char *message, gboolean is_error)
+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));
@@ -286,10 +300,39 @@ void purple_xfer_conversation_write(PurpleXfer *xfer, char *message, gboolean is
if (is_error)
flags |= PURPLE_MESSAGE_ERROR;
- purple_conversation_write(conv, NULL, escaped, flags, time(NULL));
+ 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;
@@ -448,6 +491,8 @@ 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 */
@@ -473,13 +518,19 @@ purple_xfer_ask_recv(PurpleXfer *xfer)
serv_got_im(purple_account_get_connection(xfer->account),
xfer->who, xfer->message, 0, time(NULL));
- 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));
-
+ 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);
@@ -547,10 +598,12 @@ purple_xfer_request(PurpleXfer *xfer)
{
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(xfer, message, FALSE);
+ 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);
@@ -699,6 +752,7 @@ purple_xfer_get_status(const PurpleXfer *xfer)
return xfer->status;
}
+/* FIXME: Rename with cancelled for 3.0.0. */
gboolean
purple_xfer_is_canceled(const PurpleXfer *xfer)
{
@@ -1580,6 +1634,52 @@ purple_xfer_update_progress(PurpleXfer *xfer)
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
diff --git a/libpurple/ft.h b/libpurple/ft.h
index 9ca8d65582..216eda78a6 100644
--- a/libpurple/ft.h
+++ b/libpurple/ft.h
@@ -58,8 +58,8 @@ typedef enum
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 canceled by us. */
- PURPLE_XFER_STATUS_CANCEL_REMOTE /**< The xfer was canceled by the other end, or we couldn't connect. */
+ 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;
/**
@@ -120,7 +120,12 @@ typedef struct
*/
void (*data_not_sent)(PurpleXfer *xfer, const guchar *buffer, gsize size);
- void (*_purple_reserved1)(void);
+ /**
+ * 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;
/**
@@ -299,11 +304,12 @@ const char *purple_xfer_get_remote_user(const PurpleXfer *xfer);
PurpleXferStatusType purple_xfer_get_status(const PurpleXfer *xfer);
/**
- * Returns true if the file transfer was canceled.
+ * Returns true if the file transfer was cancelled.
*
* @param xfer The file transfer.
*
- * @return Whether or not the transfer was canceled.
+ * @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);
@@ -687,6 +693,51 @@ void purple_xfer_ui_ready(PurpleXfer *xfer);
*/
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);
+
+
/*@}*/
/**************************************************************************/
diff --git a/libpurple/gconf/Makefile.am b/libpurple/gconf/Makefile.am
index 9c422d513c..ff3b98b5c9 100644
--- a/libpurple/gconf/Makefile.am
+++ b/libpurple/gconf/Makefile.am
@@ -2,17 +2,17 @@ schemadir = @GCONF_SCHEMA_FILE_DIR@
EXTRA_DIST = purple.schemas.in
-if GCONF_SCHEMAS_INSTALL
-
if INSTALL_I18N
schema_in_files = purple.schemas.in
schema_DATA = $(schema_in_files:.schemas.in=.schemas)
@INTLTOOL_SCHEMAS_RULE@
-endif #INSTALL_I18N
+if GCONF_SCHEMAS_INSTALL
install-data-local:
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
else
install-data-local:
endif #GCONF_SCHEMAS_INSTALL
+
+endif #INSTALL_I18N
diff --git a/libpurple/log.c b/libpurple/log.c
index 57eb87c004..e279e6406b 100644
--- a/libpurple/log.c
+++ b/libpurple/log.c
@@ -1681,7 +1681,6 @@ static GList *old_logger_list(PurpleLogType type, const char *sn, PurpleAccount
struct tm tm;
char month[4];
struct old_logger_data *data = NULL;
- char *newlog;
int logfound = 0;
int lastoff = 0;
int newlen;
@@ -1783,7 +1782,7 @@ static GList *old_logger_list(PurpleLogType type, const char *sn, PurpleAccount
}
while (fgets(buf, BUF_LONG, file)) {
- if ((newlog = strstr(buf, "---- New C"))) {
+ if (strstr(buf, "---- New C") != NULL) {
int length;
int offset;
char convostart[32];
diff --git a/libpurple/media.c b/libpurple/media.c
index 0bf35ca310..8234a89982 100644
--- a/libpurple/media.c
+++ b/libpurple/media.c
@@ -515,7 +515,8 @@ purple_media_add_session(PurpleMedia *media, PurpleMediaSession *session)
if (!media->priv->sessions) {
purple_debug_info("media", "Creating hash table for sessions\n");
- media->priv->sessions = g_hash_table_new(g_str_hash, g_str_equal);
+ media->priv->sessions = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, NULL);
}
g_hash_table_insert(media->priv->sessions, g_strdup(session->id), session);
}
diff --git a/libpurple/media/backend-fs2.c b/libpurple/media/backend-fs2.c
index 30ada179f2..84b1816577 100644
--- a/libpurple/media/backend-fs2.c
+++ b/libpurple/media/backend-fs2.c
@@ -208,11 +208,7 @@ purple_media_backend_fs2_dispose(GObject *obj)
}
if (priv->participants) {
- GList *participants =
- g_hash_table_get_values(priv->participants);
- for (; participants; participants = g_list_delete_link(
- participants, participants))
- g_object_unref(participants->data);
+ g_hash_table_destroy(priv->participants);
priv->participants = NULL;
}
@@ -1425,7 +1421,8 @@ create_session(PurpleMediaBackendFs2 *self, const gchar *sess_id,
if (!priv->sessions) {
purple_debug_info("backend-fs2",
"Creating hash table for sessions\n");
- priv->sessions = g_hash_table_new(g_str_hash, g_str_equal);
+ priv->sessions = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, NULL);
}
g_hash_table_insert(priv->sessions, g_strdup(session->id), session);
@@ -1461,7 +1458,7 @@ create_participant(PurpleMediaBackendFs2 *self, const gchar *name)
purple_debug_info("backend-fs2",
"Creating hash table for participants\n");
priv->participants = g_hash_table_new_full(g_str_hash,
- g_str_equal, g_free, NULL);
+ g_str_equal, g_free, g_object_unref);
}
g_hash_table_insert(priv->participants, g_strdup(name), participant);
@@ -1564,6 +1561,30 @@ src_pad_added_cb(FsStream *fsstream, GstPad *srcpad,
(GSourceFunc)src_pad_added_cb_cb, stream);
}
+static GValueArray *
+append_relay_info(GValueArray *relay_info, const gchar *ip, gint port,
+ const gchar *username, const gchar *password, const gchar *type)
+{
+ GValue value;
+ GstStructure *turn_setup = gst_structure_new("relay-info",
+ "ip", G_TYPE_STRING, ip,
+ "port", G_TYPE_UINT, port,
+ "username", G_TYPE_STRING, username,
+ "password", G_TYPE_STRING, password,
+ "relay-type", G_TYPE_STRING, type,
+ NULL);
+
+ if (turn_setup) {
+ memset(&value, 0, sizeof(GValue));
+ g_value_init(&value, GST_TYPE_STRUCTURE);
+ gst_value_set_structure(&value, turn_setup);
+ relay_info = g_value_array_append(relay_info, &value);
+ gst_structure_free(turn_setup);
+ }
+
+ return relay_info;
+}
+
static gboolean
create_stream(PurpleMediaBackendFs2 *self,
const gchar *sess_id, const gchar *who,
@@ -1605,32 +1626,22 @@ create_stream(PurpleMediaBackendFs2 *self,
if (turn_ip && !strcmp("nice", transmitter)) {
GValueArray *relay_info = g_value_array_new(0);
- GValue value;
- gint turn_port = purple_prefs_get_int(
- "/purple/network/turn_port");
+ gint port;
const gchar *username = purple_prefs_get_string(
"/purple/network/turn_username");
const gchar *password = purple_prefs_get_string(
"/purple/network/turn_password");
- GstStructure *turn_setup = gst_structure_new("relay-info",
- "ip", G_TYPE_STRING, turn_ip,
- "port", G_TYPE_UINT, turn_port,
- "username", G_TYPE_STRING, username,
- "password", G_TYPE_STRING, password,
- NULL);
- if (!turn_setup) {
- purple_debug_error("backend-fs2",
- "Error creating relay info structure");
- return FALSE;
+ /* UDP */
+ port = purple_prefs_get_int("/purple/network/turn_port");
+ if (port > 0) {
+ relay_info = append_relay_info(relay_info, turn_ip, port, username,
+ password, "udp");
}
- memset(&value, 0, sizeof(GValue));
- g_value_init(&value, GST_TYPE_STRUCTURE);
- gst_value_set_structure(&value, turn_setup);
- relay_info = g_value_array_append(relay_info, &value);
- gst_structure_free(turn_setup);
-
+ /* should add TCP and perhaps TLS relaying options when these are
+ supported by libnice using non-google mode */
+
purple_debug_info("backend-fs2",
"Setting relay-info on new stream\n");
_params[_num_params].name = "relay-info";
@@ -1639,6 +1650,7 @@ create_stream(PurpleMediaBackendFs2 *self,
g_value_set_boxed(&_params[_num_params].value,
relay_info);
g_value_array_free(relay_info);
+ _num_params++;
}
session = get_session(self, sess_id);
@@ -1790,7 +1802,7 @@ purple_media_backend_fs2_codecs_ready(PurpleMediaBackend *self,
const gchar *sess_id)
{
PurpleMediaBackendFs2Private *priv;
- gboolean ret;
+ gboolean ret = FALSE;
g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), FALSE);
@@ -1836,15 +1848,12 @@ static GList *
purple_media_backend_fs2_get_codecs(PurpleMediaBackend *self,
const gchar *sess_id)
{
- PurpleMediaBackendFs2Private *priv;
PurpleMediaBackendFs2Session *session;
GList *fscodecs;
GList *codecs;
g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), NULL);
- priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self);
-
session = get_session(PURPLE_MEDIA_BACKEND_FS2(self), sess_id);
if (session == NULL)
@@ -2013,13 +2022,10 @@ purple_media_backend_fs2_set_output_volume(PurpleMediaBackendFs2 *self,
const gchar *sess_id, const gchar *who, double level)
{
#ifdef USE_VV
- PurpleMediaBackendFs2Private *priv;
GList *streams;
g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self));
- priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self);
-
purple_prefs_set_int("/purple/media/audio/volume/output", level);
streams = get_streams(self, sess_id, who);
diff --git a/libpurple/media/backend-fs2.h b/libpurple/media/backend-fs2.h
index afd859b7e9..47fb02dac5 100644
--- a/libpurple/media/backend-fs2.h
+++ b/libpurple/media/backend-fs2.h
@@ -55,6 +55,7 @@ typedef struct _PurpleMediaBackendFs2 PurpleMediaBackendFs2;
*/
GType purple_media_backend_fs2_get_type(void);
+#ifdef USE_GSTREAMER
/*
* Temporary function in order to be able to test while
* integrating with PurpleMedia
@@ -71,6 +72,7 @@ void purple_media_backend_fs2_set_input_volume(PurpleMediaBackendFs2 *self,
void purple_media_backend_fs2_set_output_volume(PurpleMediaBackendFs2 *self,
const gchar *sess_id, const gchar *who, double level);
/* end tmp */
+#endif /* USE_GSTREAMER */
G_END_DECLS
diff --git a/libpurple/media/codec.c b/libpurple/media/codec.c
index 532f11d3dd..9eb68eca62 100644
--- a/libpurple/media/codec.c
+++ b/libpurple/media/codec.c
@@ -83,9 +83,11 @@ purple_media_codec_finalize(GObject *info)
PURPLE_MEDIA_CODEC_GET_PRIVATE(info);
g_free(priv->encoding_name);
for (; priv->optional_params; priv->optional_params =
- g_list_delete_link(priv->optional_params,
- priv->optional_params)) {
- g_free(priv->optional_params->data);
+ g_list_delete_link(priv->optional_params, priv->optional_params)) {
+ PurpleKeyValuePair *param = priv->optional_params->data;
+ g_free(param->key);
+ g_free(param->value);
+ g_free(param);
}
}
@@ -302,10 +304,10 @@ purple_media_codec_remove_optional_parameter(PurpleMediaCodec *codec,
g_free(param->key);
g_free(param->value);
- g_free(param);
priv->optional_params =
g_list_remove(priv->optional_params, param);
+ g_free(param);
}
PurpleKeyValuePair *
diff --git a/libpurple/media/codec.h b/libpurple/media/codec.h
index 771bb62d22..9262698c49 100644
--- a/libpurple/media/codec.h
+++ b/libpurple/media/codec.h
@@ -121,7 +121,8 @@ guint purple_media_codec_get_channels(PurpleMediaCodec *codec);
*
* @param The codec to get the optional parameters from.
*
- * @return The list of optional parameters.
+ * @return The list of optional parameters. The list is owned by the codec and
+ * should not be freed.
*
* @since 2.6.0
*/
diff --git a/libpurple/network.c b/libpurple/network.c
index 893f6931a6..0ceff6509f 100644
--- a/libpurple/network.c
+++ b/libpurple/network.c
@@ -98,6 +98,7 @@ struct _PurpleNetworkListenData {
PurpleNetworkListenCallback cb;
gpointer cb_data;
UPnPMappingAddRemove *mapping_data;
+ int timer;
};
#ifdef HAVE_NETWORKMANAGER
@@ -373,6 +374,7 @@ purple_network_finish_pmp_map_cb(gpointer data)
gint *value = g_new(gint, 1);
listen_data = data;
+ listen_data->timer = 0;
/* add port mapping to hash table */
*key = purple_network_get_port_from_fd(listen_data->listenfd);
@@ -394,7 +396,7 @@ void purple_network_listen_map_external(gboolean map_external)
}
static PurpleNetworkListenData *
-purple_network_do_listen(unsigned short port, int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data)
+purple_network_do_listen(unsigned short port, int socket_family, int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data)
{
int listenfd = -1;
int flags;
@@ -412,7 +414,7 @@ purple_network_do_listen(unsigned short port, int socket_type, PurpleNetworkList
g_snprintf(serv, sizeof(serv), "%hu", port);
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_flags = AI_PASSIVE;
- hints.ai_family = AF_UNSPEC;
+ hints.ai_family = socket_family;
hints.ai_socktype = socket_type;
errnum = getaddrinfo(NULL /* any IP */, serv, &hints, &res);
if (errnum != 0) {
@@ -436,7 +438,7 @@ purple_network_do_listen(unsigned short port, int socket_type, PurpleNetworkList
if (listenfd < 0)
continue;
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0)
- purple_debug_warning("network", "setsockopt: %s\n", g_strerror(errno));
+ purple_debug_warning("network", "setsockopt(SO_REUSEADDR): %s\n", g_strerror(errno));
if (bind(listenfd, next->ai_addr, next->ai_addrlen) == 0)
break; /* success */
/* XXX - It is unclear to me (datallah) whether we need to be
@@ -451,6 +453,13 @@ purple_network_do_listen(unsigned short port, int socket_type, PurpleNetworkList
#else
struct sockaddr_in sockin;
+ if (socket_family != AF_INET && socket_family != AF_UNSPEC) {
+ purple_debug_warning("network", "Address family %d only "
+ "supported when built with getaddrinfo() "
+ "support\n", socket_family);
+ return NULL;
+ }
+
if ((listenfd = socket(AF_INET, socket_type, 0)) < 0) {
purple_debug_warning("network", "socket: %s\n", g_strerror(errno));
return NULL;
@@ -492,11 +501,12 @@ purple_network_do_listen(unsigned short port, int socket_type, PurpleNetworkList
listen_data->cb_data = cb_data;
listen_data->socket_type = socket_type;
- if (!listen_map_external || !purple_prefs_get_bool("/purple/network/map_ports"))
+ if (!purple_socket_speaks_ipv4(listenfd) || !listen_map_external ||
+ !purple_prefs_get_bool("/purple/network/map_ports"))
{
purple_debug_info("network", "Skipping external port mapping.\n");
/* The pmp_map_cb does what we want to do */
- purple_timeout_add(0, purple_network_finish_pmp_map_cb, listen_data);
+ listen_data->timer = purple_timeout_add(0, purple_network_finish_pmp_map_cb, listen_data);
}
/* Attempt a NAT-PMP Mapping, which will return immediately */
else if (purple_pmp_create_map(((socket_type == SOCK_STREAM) ? PURPLE_PMP_TYPE_TCP : PURPLE_PMP_TYPE_UDP),
@@ -504,7 +514,7 @@ purple_network_do_listen(unsigned short port, int socket_type, PurpleNetworkList
{
purple_debug_info("network", "Created NAT-PMP mapping on port %i\n", actual_port);
/* We want to return listen_data now, and on the next run loop trigger the cb and destroy listen_data */
- purple_timeout_add(0, purple_network_finish_pmp_map_cb, listen_data);
+ listen_data->timer = purple_timeout_add(0, purple_network_finish_pmp_map_cb, listen_data);
}
else
{
@@ -519,17 +529,29 @@ purple_network_do_listen(unsigned short port, int socket_type, PurpleNetworkList
}
PurpleNetworkListenData *
-purple_network_listen(unsigned short port, int socket_type,
- PurpleNetworkListenCallback cb, gpointer cb_data)
+purple_network_listen_family(unsigned short port, int socket_family,
+ int socket_type, PurpleNetworkListenCallback cb,
+ gpointer cb_data)
{
g_return_val_if_fail(port != 0, NULL);
- return purple_network_do_listen(port, socket_type, cb, cb_data);
+ return purple_network_do_listen(port, socket_family, socket_type,
+ cb, cb_data);
}
PurpleNetworkListenData *
-purple_network_listen_range(unsigned short start, unsigned short end,
- int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data)
+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,
+ PurpleNetworkListenCallback cb,
+ gpointer cb_data)
{
PurpleNetworkListenData *ret = NULL;
@@ -542,7 +564,7 @@ purple_network_listen_range(unsigned short start, unsigned short end,
}
for (; start <= end; start++) {
- ret = purple_network_do_listen(start, socket_type, cb, cb_data);
+ ret = purple_network_do_listen(start, AF_UNSPEC, socket_type, cb, cb_data);
if (ret != NULL)
break;
}
@@ -550,11 +572,23 @@ purple_network_listen_range(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)
purple_upnp_cancel_port_mapping(listen_data->mapping_data);
+ if (listen_data->timer > 0)
+ purple_timeout_remove(listen_data->timer);
+
g_free(listen_data);
}
diff --git a/libpurple/network.h b/libpurple/network.h
index 13f891a529..f836d88520 100644
--- a/libpurple/network.h
+++ b/libpurple/network.h
@@ -90,8 +90,8 @@ const char *purple_network_get_local_system_ip(int fd);
/**
* Returns all IP addresses of the local system.
*
- * @note The caller must free this list, this function currently only
- * handles IPv4 addresses
+ * @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.
@@ -123,8 +123,8 @@ const char *purple_network_get_my_ip(int fd);
* 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 above functions to
- * perform the same function.
+ * @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);
@@ -138,8 +138,8 @@ void purple_network_listen_map_external(gboolean map_external);
*
* This opens a listening port. The caller will want to set up a watcher
* of type PURPLE_INPUT_READ on the fd returned in cb. It will probably call
- * accept in the watcher callback, and then possibly remove the watcher and close
- * the listening socket, and add a new watcher on the new socket accept
+ * accept in the watcher callback, and then possibly remove the watcher and
+ * 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.
@@ -158,6 +158,27 @@ 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.
+ */
+PurpleNetworkListenData *purple_network_listen_family(unsigned short port,
+ int socket_family, int socket_type, PurpleNetworkListenCallback cb,
+ gpointer cb_data);
+
+/**
* 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,11 +213,33 @@ PurpleNetworkListenData *purple_network_listen_range(unsigned short start,
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.
+ */
+PurpleNetworkListenData *purple_network_listen_range_family(
+ unsigned short start, unsigned short end, int socket_family,
+ int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data);
+
+/**
* 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 canceled and
+ * @param listen_data This listener attempt will be cancelled and
* the struct will be freed.
*/
void purple_network_listen_cancel(PurpleNetworkListenData *listen_data);
@@ -236,15 +279,15 @@ void purple_network_force_online(void);
*/
void *purple_network_get_handle(void);
-/**
+/**
* 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);
-
+
/**
* Get the IP address of the STUN server as a string representation
*
@@ -252,16 +295,16 @@ void purple_network_set_stun_server(const gchar *stun_server);
* @since 2.6.0
*/
const gchar *purple_network_get_stun_ip(void);
-
-/**
+
+/**
* 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);
-
+
/**
* Get the IP address of the TURN server as a string representation
*
@@ -269,14 +312,14 @@ void purple_network_set_turn_server(const gchar *turn_server);
* @since 2.6.0
*/
const gchar *purple_network_get_turn_ip(void);
-
+
/**
* Remove a port mapping (UPnP or NAT-PMP) associated with listening socket
*
* @param fd Socket to remove the port mapping for
* @since 2.6.0
*/
-void purple_network_remove_port_mapping(gint fd);
+void purple_network_remove_port_mapping(gint fd);
/**
* Convert a UTF-8 domain name to ASCII in accordance with the IDNA
diff --git a/libpurple/plugins/Makefile.mingw b/libpurple/plugins/Makefile.mingw
index b3c80a078b..a62253fedb 100644
--- a/libpurple/plugins/Makefile.mingw
+++ b/libpurple/plugins/Makefile.mingw
@@ -55,7 +55,7 @@ install: all $(PURPLE_INSTALL_PLUGINS_DIR)
$(MAKE) -C $(SSL_PLUGIN) -f $(MINGW_MAKEFILE) install
cp *.dll $(PURPLE_INSTALL_PLUGINS_DIR)
-.c.dll:
+%.dll: %.c $(PURPLE_CONFIG_H) $(PURPLE_VERSION_H)
$(CC) $(CFLAGS) $(DEFINES) $(INCLUDE_PATHS) -o $@.o -c $<
$(CC) -shared $@.o $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $@
diff --git a/libpurple/plugins/idle.c b/libpurple/plugins/idle.c
index 8d3c72e9ca..0860dafa82 100644
--- a/libpurple/plugins/idle.c
+++ b/libpurple/plugins/idle.c
@@ -110,9 +110,6 @@ idle_all_action_ok(void *ignored, PurpleRequestFields *fields)
for(iter = list; iter; iter = iter->next) {
acct = (PurpleAccount *)(iter->data);
- if(acct)
- prpl_id = purple_account_get_protocol_id(acct);
-
if(acct && idleable_filter(acct)) {
purple_debug_misc("idle", "Idling %s.\n",
purple_account_get_username(acct));
diff --git a/libpurple/plugins/perl/common/Log.xs b/libpurple/plugins/perl/common/Log.xs
index 93a68d46b6..6b4b82635c 100644
--- a/libpurple/plugins/perl/common/Log.xs
+++ b/libpurple/plugins/perl/common/Log.xs
@@ -27,6 +27,9 @@ BOOT:
newCONSTSUB(flags_stash, (char *)civ->name, newSViv(civ->iv));
}
+Purple::Handle
+purple_log_get_handle()
+
int
purple_log_common_sizer(log)
Purple::Log log
diff --git a/libpurple/plugins/signals-test.c b/libpurple/plugins/signals-test.c
index 5e64015a4c..2992d4dc15 100644
--- a/libpurple/plugins/signals-test.c
+++ b/libpurple/plugins/signals-test.c
@@ -592,12 +592,12 @@ ft_send_start_cb(PurpleXfer *xfer, gpointer data) {
static void
ft_recv_cancel_cb(PurpleXfer *xfer, gpointer data) {
- purple_debug_misc("signals test", "file receive canceled\n");
+ purple_debug_misc("signals test", "file receive cancelled\n");
}
static void
ft_send_cancel_cb(PurpleXfer *xfer, gpointer data) {
- purple_debug_misc("signals test", "file send canceled\n");
+ purple_debug_misc("signals test", "file send cancelled\n");
}
static void
diff --git a/libpurple/plugins/tcl/tcl_cmd.c b/libpurple/plugins/tcl/tcl_cmd.c
index 606dc36648..fc0d1c3bb9 100644
--- a/libpurple/plugins/tcl/tcl_cmd.c
+++ b/libpurple/plugins/tcl/tcl_cmd.c
@@ -125,7 +125,7 @@ static PurpleCmdRet tcl_cmd_callback(PurpleConversation *conv, const gchar *cmd,
gchar **args, gchar **errors,
struct tcl_cmd_handler *handler)
{
- int retval, error, i;
+ int retval, i;
Tcl_Obj *command, *arg, *tclargs, *result;
command = Tcl_NewListObj(0, NULL);
@@ -153,8 +153,7 @@ static PurpleCmdRet tcl_cmd_callback(PurpleConversation *conv, const gchar *cmd,
}
Tcl_ListObjAppendElement(handler->interp, command, tclargs);
- if ((error = Tcl_EvalObjEx(handler->interp, command,
- TCL_EVAL_GLOBAL)) != TCL_OK) {
+ if (Tcl_EvalObjEx(handler->interp, command, TCL_EVAL_GLOBAL) != TCL_OK) {
gchar *errorstr;
errorstr = g_strdup_printf("error evaluating callback: %s\n",
@@ -164,8 +163,8 @@ static PurpleCmdRet tcl_cmd_callback(PurpleConversation *conv, const gchar *cmd,
retval = PURPLE_CMD_RET_FAILED;
} else {
result = Tcl_GetObjResult(handler->interp);
- if ((error = Tcl_GetIntFromObj(handler->interp, result,
- &retval)) != TCL_OK) {
+ if (Tcl_GetIntFromObj(handler->interp, result,
+ &retval) != TCL_OK) {
gchar *errorstr;
errorstr = g_strdup_printf("Error retreiving procedure result: %s\n",
diff --git a/libpurple/plugins/tcl/tcl_signals.c b/libpurple/plugins/tcl/tcl_signals.c
index 98c27d8294..c3d6d0fddf 100644
--- a/libpurple/plugins/tcl/tcl_signals.c
+++ b/libpurple/plugins/tcl/tcl_signals.c
@@ -160,7 +160,7 @@ static void *tcl_signal_callback(va_list args, struct tcl_signal_handler *handle
{
GString *name, *val;
PurpleBlistNode *node;
- int error, i;
+ int i;
void *retval = NULL;
Tcl_Obj *cmd, *arg, *result;
void **vals; /* Used for inout parameters */
@@ -335,7 +335,7 @@ static void *tcl_signal_callback(va_list args, struct tcl_signal_handler *handle
}
/* Call the friggin' procedure already */
- if ((error = Tcl_EvalObjEx(handler->interp, cmd, TCL_EVAL_GLOBAL)) != TCL_OK) {
+ if (Tcl_EvalObjEx(handler->interp, cmd, TCL_EVAL_GLOBAL) != TCL_OK) {
purple_debug(PURPLE_DEBUG_ERROR, "tcl", "error evaluating callback: %s\n",
Tcl_GetString(Tcl_GetObjResult(handler->interp)));
} else {
@@ -345,7 +345,7 @@ static void *tcl_signal_callback(va_list args, struct tcl_signal_handler *handle
if (purple_value_get_type(handler->returntype) == PURPLE_TYPE_STRING) {
retval = (void *)g_strdup(Tcl_GetString(result));
} else {
- if ((error = Tcl_GetIntFromObj(handler->interp, result, (int *)&retval)) != TCL_OK) {
+ if (Tcl_GetIntFromObj(handler->interp, result, (int *)&retval) != TCL_OK) {
purple_debug(PURPLE_DEBUG_ERROR, "tcl", "Error retrieving procedure result: %s\n",
Tcl_GetString(Tcl_GetObjResult(handler->interp)));
retval = NULL;
diff --git a/libpurple/prefs.c b/libpurple/prefs.c
index 5ef0c07c08..f02d2d18da 100644
--- a/libpurple/prefs.c
+++ b/libpurple/prefs.c
@@ -506,7 +506,6 @@ pref_full_name(struct purple_pref *pref)
return g_strdup("/");
name = g_string_new(pref->name);
- parent = pref->parent;
for(parent = pref->parent; parent && parent->name; parent = parent->parent) {
name = g_string_prepend_c(name, '/');
diff --git a/libpurple/protocols/bonjour/bonjour.c b/libpurple/protocols/bonjour/bonjour.c
index eb72a1141d..8745ceddbe 100644
--- a/libpurple/protocols/bonjour/bonjour.c
+++ b/libpurple/protocols/bonjour/bonjour.c
@@ -70,7 +70,6 @@ bonjour_removeallfromlocal(PurpleConnection *conn, PurpleGroup *bonjour_group)
buddy = (PurpleBuddy *) bnode;
if (purple_buddy_get_account(buddy) != account)
continue;
- purple_prpl_got_user_status(account, purple_buddy_get_name(buddy), "offline", NULL);
purple_account_remove_buddy(account, buddy, NULL);
purple_blist_remove_buddy(buddy);
}
@@ -172,7 +171,9 @@ bonjour_close(PurpleConnection *connection)
g_free(bd->jabber_data);
}
- /* Delete the bonjour group */
+ /* Delete the bonjour group
+ * (purple_blist_remove_group will bail out if the group isn't empty)
+ */
if (bonjour_group != NULL)
purple_blist_remove_group(bonjour_group);
@@ -205,18 +206,12 @@ bonjour_set_status(PurpleAccount *account, PurpleStatus *status)
{
PurpleConnection *gc;
BonjourData *bd;
- gboolean disconnected;
- PurpleStatusType *type;
- int primitive;
PurplePresence *presence;
const char *message, *bonjour_status;
gchar *stripped;
gc = purple_account_get_connection(account);
bd = gc->proto_data;
- disconnected = purple_account_is_disconnected(account);
- type = purple_status_get_type(status);
- primitive = purple_status_type_get_primitive(type);
presence = purple_account_get_presence(account);
message = purple_status_get_attr_string(status, "message");
@@ -528,7 +523,9 @@ static PurplePluginProtocolInfo prpl_info =
NULL, /* get_account_text_table */
NULL, /* initiate_media */
NULL, /* get_media_caps */
- NULL /* get_moods */
+ NULL, /* get_moods */
+ NULL, /* set_public_alias */
+ NULL /* get_public_alias */
};
static PurplePluginInfo info =
diff --git a/libpurple/protocols/bonjour/jabber.c b/libpurple/protocols/bonjour/jabber.c
index 7f1c313799..7b5496faef 100644
--- a/libpurple/protocols/bonjour/jabber.c
+++ b/libpurple/protocols/bonjour/jabber.c
@@ -386,7 +386,7 @@ static void bonjour_jabber_stream_ended(BonjourJabberConversation *bconv) {
BonjourBuddy *bb = NULL;
const gchar *name = bconv->pb ? purple_buddy_get_name(bconv->pb) : "(unknown)";
- purple_debug_info("bonjour", "Recieved conversation close notification from %s.\n", name);
+ purple_debug_info("bonjour", "Received conversation close notification from %s.\n", name);
if(bconv->pb != NULL)
bb = purple_buddy_get_protocol_data(bconv->pb);
@@ -582,7 +582,7 @@ static gboolean bonjour_jabber_send_stream_init(BonjourJabberConversation *bconv
}
/* This gets called when we've successfully sent our <stream:stream />
- * AND when we've recieved a <stream:stream /> */
+ * AND when we've received a <stream:stream /> */
void bonjour_jabber_stream_started(BonjourJabberConversation *bconv) {
if (bconv->sent_stream_start == NOT_SENT && !bonjour_jabber_send_stream_init(bconv, bconv->socket)) {
@@ -683,7 +683,7 @@ _server_socket_handler(gpointer data, int server_socket, PurpleInputCondition co
g_slist_free(buddies);
if (mbba->matched_buddies == NULL) {
- purple_debug_info("bonjour", "We don't like invisible buddies, this is not a superheros comic\n");
+ purple_debug_info("bonjour", "We don't like invisible buddies, this is not a superheroes comic\n");
g_free(mbba);
close(client_socket);
return;
@@ -1184,7 +1184,7 @@ bonjour_jabber_stop(BonjourJabber *jdata)
buddies = purple_find_buddies(jdata->account, NULL);
for (l = buddies; l; l = l->next) {
BonjourBuddy *bb = purple_buddy_get_protocol_data((PurpleBuddy*) l->data);
- if (bb != NULL) {
+ if (bb && bb->conversation) {
/* Any ongoing connection attempt is cancelled
* by _purple_connection_destroy */
bb->conversation->connect_data = NULL;
@@ -1273,7 +1273,6 @@ check_if_blocked(PurpleBuddy *pb)
static void
xep_iq_parse(xmlnode *packet, PurpleBuddy *pb)
{
- xmlnode *child;
PurpleAccount *account;
PurpleConnection *gc;
@@ -1283,7 +1282,7 @@ xep_iq_parse(xmlnode *packet, PurpleBuddy *pb)
account = purple_buddy_get_account(pb);
gc = purple_account_get_connection(account);
- if ((child = xmlnode_get_child(packet, "si")) || (child = xmlnode_get_child(packet, "error")))
+ if (xmlnode_get_child(packet, "si") != NULL || xmlnode_get_child(packet, "error") != NULL)
xep_si_parse(gc, packet, pb);
else
xep_bytestreams_parse(gc, packet, pb);
diff --git a/libpurple/protocols/bonjour/mdns_avahi.c b/libpurple/protocols/bonjour/mdns_avahi.c
index 7ef358cbea..83d094ad82 100644
--- a/libpurple/protocols/bonjour/mdns_avahi.c
+++ b/libpurple/protocols/bonjour/mdns_avahi.c
@@ -115,7 +115,6 @@ _resolver_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtoco
AvahiStringList *l;
size_t size;
char *key, *value;
- int ret;
char ip[AVAHI_ADDRESS_STR_MAX];
AvahiBuddyImplData *b_impl;
AvahiSvcResolverData *rd;
@@ -202,7 +201,7 @@ _resolver_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtoco
/* Obtain the parameters from the text_record */
clear_bonjour_buddy_values(bb);
for(l = txt; l != NULL; l = l->next) {
- if ((ret = avahi_string_list_get_pair(l, &key, &value, &size)) < 0)
+ if (avahi_string_list_get_pair(l, &key, &value, &size) < 0)
continue;
set_bonjour_buddy_value(bb, key, value, size);
/* TODO: Since we're using the glib allocator, I think we
diff --git a/libpurple/protocols/gg/Makefile.am b/libpurple/protocols/gg/Makefile.am
index 7bc99c9563..5fe288fb3d 100644
--- a/libpurple/protocols/gg/Makefile.am
+++ b/libpurple/protocols/gg/Makefile.am
@@ -9,6 +9,7 @@ EXTRA_DIST = \
lib/http.c \
lib/libgadu.c \
lib/libgadu-config.h \
+ lib/libgadu-internal.h \
lib/libgadu.h \
lib/obsolete.c \
lib/protocol.h \
@@ -30,6 +31,7 @@ INTGGSOURCES = \
lib/http.c \
lib/libgadu.c \
lib/libgadu-config.h \
+ lib/libgadu-internal.h \
lib/libgadu.h \
lib/obsolete.c \
lib/protocol.h \
diff --git a/libpurple/protocols/gg/gg.c b/libpurple/protocols/gg/gg.c
index 2ec6a8983d..9e36aa4c5a 100644
--- a/libpurple/protocols/gg/gg.c
+++ b/libpurple/protocols/gg/gg.c
@@ -1000,7 +1000,6 @@ static void ggp_generic_status_handler(PurpleConnection *gc, uin_t uin,
int status, const char *descr)
{
gchar *from;
- gchar *msg;
const char *st;
gchar *avatarurl;
PurpleUtilFetchUrlData *url_data;
@@ -1035,6 +1034,7 @@ static void ggp_generic_status_handler(PurpleConnection *gc, uin_t uin,
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";
@@ -1048,11 +1048,14 @@ static void ggp_generic_status_handler(PurpleConnection *gc, uin_t uin,
purple_debug_info("gg", "st = %s\n", st);
//msg = charset_convert(descr, "CP1250", "UTF-8");
- msg = g_strdup_printf("%s", descr);
- purple_prpl_got_user_status(purple_connection_get_account(gc),
- from, st, "message", msg, NULL);
+ if (descr == 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", descr, NULL);
+ }
g_free(from);
- g_free(msg);
}
static void ggp_sr_close_cb(gpointer user_data)
@@ -1598,22 +1601,16 @@ static void ggp_callback_recv(gpointer _gc, gint fd, PurpleInputCondition cond)
}
break;
case GG_EVENT_NOTIFY60:
- purple_debug_info("gg",
- "notify60_pre: (%d) status=%d; version=%d; descr=%s\n",
- ev->event.notify60->uin, GG_S(ev->event.notify60->status),
- ev->event.notify60->version,
- ev->event.notify60->descr ? ev->event.notify60->descr : "(null)");
-
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,
- ev->event.notify60[i].status,
+ 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,
- ev->event.notify60[i].status,
+ GG_S(ev->event.notify60[i].status),
ev->event.notify60[i].descr);
}
break;
@@ -1623,7 +1620,7 @@ static void ggp_callback_recv(gpointer _gc, gint fd, PurpleInputCondition cond)
ev->event.status.descr ? ev->event.status.descr : "(null)");
ggp_generic_status_handler(gc, ev->event.status.uin,
- ev->event.status.status, ev->event.status.descr);
+ GG_S(ev->event.status.status), ev->event.status.descr);
break;
case GG_EVENT_STATUS60:
purple_debug_info("gg",
@@ -1734,7 +1731,7 @@ static void ggp_async_login_handler(gpointer _gc, gint fd, PurpleInputCondition
ggp_callback_recv, gc);
ggp_buddylist_send(gc);
- purple_connection_update_progress(gc, _("Connected"), 2, 2);
+ purple_connection_update_progress(gc, _("Connected"), 1, 2);
purple_connection_set_state(gc, PURPLE_CONNECTED);
}
break;
@@ -1987,7 +1984,7 @@ static void ggp_login(PurpleAccount *account)
purple_debug_info("gg", "Trying to retrieve address from gg appmsg service\n");
info->session = gg_login(glp);
- purple_connection_update_progress(gc, _("Connecting"), 1, 2);
+ purple_connection_update_progress(gc, _("Connecting"), 0, 2);
if (info->session == NULL) {
purple_connection_error_reason (gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
@@ -2509,7 +2506,9 @@ static PurplePluginProtocolInfo prpl_info =
NULL, /* get_account_text_table */
NULL, /* initiate_media */
NULL, /* can_do_media */
- NULL /* get_moods */
+ NULL, /* get_moods */
+ NULL, /* set_public_alias */
+ NULL /* get_public_alias */
};
static PurplePluginInfo info = {
diff --git a/libpurple/protocols/gg/lib/common.c b/libpurple/protocols/gg/lib/common.c
index 63885b5d00..7e3ccd2337 100644
--- a/libpurple/protocols/gg/lib/common.c
+++ b/libpurple/protocols/gg/lib/common.c
@@ -101,7 +101,7 @@ FILE *gg_debug_file = NULL;
* \param format Format wiadomości (zgodny z \c printf)
* \param ap Lista argumentĂłw (zgodna z \c printf)
*/
-static void gg_debug_common(struct gg_session *sess, int level, const char *format, va_list ap)
+void gg_debug_common(struct gg_session *sess, int level, const char *format, va_list ap)
{
if (gg_debug_handler_session)
(*gg_debug_handler_session)(sess, level, format, ap);
diff --git a/libpurple/protocols/gg/lib/events.c b/libpurple/protocols/gg/lib/events.c
index b382362842..1bf30aff54 100644
--- a/libpurple/protocols/gg/lib/events.c
+++ b/libpurple/protocols/gg/lib/events.c
@@ -1143,7 +1143,7 @@ static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e)
case GG_NOTIFY_REPLY80:
{
struct gg_notify_reply80 *n = (void*) p;
- int length = h->length, i = 0;
+ unsigned int length = h->length, i = 0;
gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
diff --git a/libpurple/protocols/gg/lib/libgadu-internal.h b/libpurple/protocols/gg/lib/libgadu-internal.h
index 07628f5122..202f89a3b9 100644
--- a/libpurple/protocols/gg/lib/libgadu-internal.h
+++ b/libpurple/protocols/gg/lib/libgadu-internal.h
@@ -26,5 +26,6 @@
char *gg_cp_to_utf8(const char *b);
char *gg_utf8_to_cp(const char *b);
int gg_pubdir50_handle_reply_sess(struct gg_session *sess, struct gg_event *e, const char *packet, int length);
+void gg_debug_common(struct gg_session *sess, int level, const char *format, va_list ap);
#endif /* LIBGADU_INTERNAL_H */
diff --git a/libpurple/protocols/gg/lib/libgadu.c b/libpurple/protocols/gg/lib/libgadu.c
index 313afd23fc..28d95587a6 100644
--- a/libpurple/protocols/gg/lib/libgadu.c
+++ b/libpurple/protocols/gg/lib/libgadu.c
@@ -412,7 +412,8 @@ void *gg_recv_packet(struct gg_session *sess)
{
struct gg_header h;
char *buf = NULL;
- int ret = 0, offset, size = 0;
+ int ret = 0;
+ unsigned int offset, size = 0;
gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess);
@@ -609,7 +610,7 @@ int gg_send_packet(struct gg_session *sess, int type, ...)
h->length = gg_fix32(tmp_length - sizeof(struct gg_header));
if ((gg_debug_level & GG_DEBUG_DUMP)) {
- int i;
+ unsigned int i;
gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_send_packet(0x%.2x)", gg_fix32(h->type));
for (i = 0; i < tmp_length; ++i)
diff --git a/libpurple/protocols/gg/lib/libgadu.h b/libpurple/protocols/gg/lib/libgadu.h
index 8a06bb5837..7d84f736ef 100644
--- a/libpurple/protocols/gg/lib/libgadu.h
+++ b/libpurple/protocols/gg/lib/libgadu.h
@@ -117,7 +117,7 @@ typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
-#ifndef __CYGWIN__
+#if !defined(__CYGWIN__) && !defined(__SunOS)
#define __int8_t_defined
typedef signed char int8_t;
typedef signed short int16_t;
@@ -1245,8 +1245,8 @@ struct gg_http *gg_change_passwd2(uin_t uin, const char *passwd, const char *new
struct gg_http *gg_change_passwd3(uin_t uin, const char *passwd, const char *newpasswd, const char *qa, int async) GG_DEPRECATED;
int gg_resolve(int *fd, int *pid, const char *hostname) GG_DEPRECATED;
-void gg_resolve_pthread_cleanup(void *arg, int kill) GG_DEPRECATED;
int gg_resolve_pthread(int *fd, void **resolver, const char *hostname) GG_DEPRECATED;
+void gg_resolve_pthread_cleanup(void *arg, int kill) GG_DEPRECATED;
struct gg_change_info_request {
char *first_name;
diff --git a/libpurple/protocols/gg/lib/resolver.c b/libpurple/protocols/gg/lib/resolver.c
index 6721f92779..ce1c270faa 100644
--- a/libpurple/protocols/gg/lib/resolver.c
+++ b/libpurple/protocols/gg/lib/resolver.c
@@ -551,9 +551,9 @@ static int gg_resolver_fork_start(int *fd, void **priv_data, const char *hostnam
}
if (write(pipes[1], &addr, sizeof(addr)) != sizeof(addr))
- exit(1);
+ _exit(1);
- exit(0);
+ _exit(0);
}
close(pipes[1]);
diff --git a/libpurple/protocols/gg/search.c b/libpurple/protocols/gg/search.c
index 55488d728e..7ecb7b4ff5 100644
--- a/libpurple/protocols/gg/search.c
+++ b/libpurple/protocols/gg/search.c
@@ -207,7 +207,7 @@ char *ggp_search_get_result(gg_pubdir50_t res, int num, const char *field)
{
char *tmp;
- tmp = charset_convert(gg_pubdir50_get(res, num, field), "CP1250", "UTF-8");
+ tmp = g_strdup(gg_pubdir50_get(res, num, field));
return (tmp == NULL) ? g_strdup("") : tmp;
}
diff --git a/libpurple/protocols/irc/cmds.c b/libpurple/protocols/irc/cmds.c
index 902a98bcf0..60041f9d55 100644
--- a/libpurple/protocols/irc/cmds.c
+++ b/libpurple/protocols/irc/cmds.c
@@ -238,16 +238,16 @@ int irc_cmd_mode(struct irc_conn *irc, const char *cmd, const char *target, cons
if (!args[0] && irc_ischannel(target))
buf = irc_format(irc, "vc", "MODE", target);
else if (args[0] && (*args[0] == '+' || *args[0] == '-'))
- buf = irc_format(irc, "vcv", "MODE", target, args[0]);
+ buf = irc_format(irc, "vcn", "MODE", target, args[0]);
else if (args[0])
- buf = irc_format(irc, "vv", "MODE", args[0]);
+ buf = irc_format(irc, "vn", "MODE", args[0]);
else
return 0;
} else if (!strcmp(cmd, "umode")) {
if (!args[0])
return 0;
gc = purple_account_get_connection(irc->account);
- buf = irc_format(irc, "vnv", "MODE", purple_connection_get_display_name(gc), args[0]);
+ buf = irc_format(irc, "vnc", "MODE", purple_connection_get_display_name(gc), args[0]);
} else {
return 0;
}
@@ -437,7 +437,7 @@ int irc_cmd_quote(struct irc_conn *irc, const char *cmd, const char *target, con
if (!args || !args[0])
return 0;
- buf = irc_format(irc, "v", args[0]);
+ buf = irc_format(irc, "n", args[0]);
irc_send(irc, buf);
g_free(buf);
diff --git a/libpurple/protocols/irc/irc.c b/libpurple/protocols/irc/irc.c
index 563c7ec2d2..dc6399f73a 100644
--- a/libpurple/protocols/irc/irc.c
+++ b/libpurple/protocols/irc/irc.c
@@ -945,7 +945,9 @@ static PurplePluginProtocolInfo prpl_info =
NULL, /* get_account_text_table */
NULL, /* initiate_media */
NULL, /* get_media_caps */
- NULL /* get_moods */
+ NULL, /* get_moods */
+ NULL, /* set_public_alias */
+ NULL /* get_public_alias */
};
static gboolean load_plugin (PurplePlugin *plugin) {
diff --git a/libpurple/protocols/irc/msgs.c b/libpurple/protocols/irc/msgs.c
index 30b3870129..2f4651b93c 100644
--- a/libpurple/protocols/irc/msgs.c
+++ b/libpurple/protocols/irc/msgs.c
@@ -851,7 +851,7 @@ void irc_msg_kick(struct irc_conn *irc, const char *name, const char *from, char
}
if (!convo) {
- purple_debug(PURPLE_DEBUG_ERROR, "irc", "Recieved a KICK for unknown channel %s\n", args[0]);
+ purple_debug(PURPLE_DEBUG_ERROR, "irc", "Received a KICK for unknown channel %s\n", args[0]);
g_free(nick);
return;
}
diff --git a/libpurple/protocols/jabber/auth.c b/libpurple/protocols/jabber/auth.c
index 1dae3aed98..4ecbe55608 100644
--- a/libpurple/protocols/jabber/auth.c
+++ b/libpurple/protocols/jabber/auth.c
@@ -45,35 +45,6 @@ static void auth_old_result_cb(JabberStream *js, const char *from,
JabberIqType type, const char *id,
xmlnode *packet, gpointer data);
-gboolean
-jabber_process_starttls(JabberStream *js, xmlnode *packet)
-{
- PurpleAccount *account;
- xmlnode *starttls;
-
- account = purple_connection_get_account(js->gc);
-
- if((starttls = xmlnode_get_child(packet, "starttls"))) {
- if(purple_ssl_is_supported()) {
- jabber_send_raw(js,
- "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>", -1);
- return TRUE;
- } else if(xmlnode_get_child(starttls, "required")) {
- purple_connection_error_reason(js->gc,
- PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
- _("Server requires TLS/SSL, but no TLS/SSL support was found."));
- return TRUE;
- } else if(purple_account_get_bool(account, "require_tls", JABBER_DEFAULT_REQUIRE_TLS)) {
- purple_connection_error_reason(js->gc,
- PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
- _("You require encryption, but no TLS/SSL support was found."));
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
static void finish_plaintext_authentication(JabberStream *js)
{
JabberIq *iq;
@@ -152,7 +123,7 @@ auth_no_pass_cb(PurpleConnection *gc, PurpleRequestFields *fields)
if (!PURPLE_CONNECTION_IS_VALID(gc))
return;
- /* Disable the account as the user has canceled connecting */
+ /* Disable the account as the user has cancelled connecting */
purple_account_set_enabled(purple_connection_get_account(gc), purple_core_get_ui(), FALSE);
}
#endif
@@ -187,7 +158,7 @@ jabber_auth_start(JabberStream *js, xmlnode *packet)
if (mech_name && *mech_name)
mechanisms = g_slist_prepend(mechanisms, mech_name);
- else if (mech_name)
+ else
g_free(mech_name);
}
@@ -208,6 +179,11 @@ jabber_auth_start(JabberStream *js, xmlnode *packet)
}
}
+ while (mechanisms) {
+ g_free(mechanisms->data);
+ mechanisms = g_slist_delete_link(mechanisms, mechanisms);
+ }
+
if (js->auth_mech == NULL) {
/* Found no good mechanisms... */
purple_connection_error_reason(js->gc,
@@ -275,7 +251,8 @@ static void auth_old_cb(JabberStream *js, const char *from,
g_free(msg);
} else if (type == JABBER_IQ_RESULT) {
query = xmlnode_get_child(packet, "query");
- if(js->stream_id && xmlnode_get_child(query, "digest")) {
+ if (js->stream_id && *js->stream_id &&
+ xmlnode_get_child(query, "digest")) {
char *s, *hash;
iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:auth");
@@ -293,8 +270,10 @@ static void auth_old_cb(JabberStream *js, const char *from,
g_free(s);
jabber_iq_set_callback(iq, auth_old_result_cb, NULL);
jabber_iq_send(iq);
-
- } else if(js->stream_id && (x = xmlnode_get_child(query, "crammd5"))) {
+ } else if ((x = 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;
@@ -364,7 +343,8 @@ void jabber_auth_start_old(JabberStream *js)
* is requiring SSL/TLS, we need to enforce it.
*/
if (!jabber_stream_is_ssl(js) &&
- purple_account_get_bool(account, "require_tls", JABBER_DEFAULT_REQUIRE_TLS)) {
+ 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_ENCRYPTION_ERROR,
_("You require encryption, but it is not available on this server."));
diff --git a/libpurple/protocols/jabber/auth.h b/libpurple/protocols/jabber/auth.h
index af0b468346..f798c51bbc 100644
--- a/libpurple/protocols/jabber/auth.h
+++ b/libpurple/protocols/jabber/auth.h
@@ -45,7 +45,6 @@ struct _JabberSaslMech {
void (*dispose)(JabberStream *js);
};
-gboolean jabber_process_starttls(JabberStream *js, xmlnode *packet);
void jabber_auth_start(JabberStream *js, xmlnode *packet);
void jabber_auth_start_old(JabberStream *js);
void jabber_auth_handle_challenge(JabberStream *js, xmlnode *packet);
diff --git a/libpurple/protocols/jabber/auth_cyrus.c b/libpurple/protocols/jabber/auth_cyrus.c
index 0a8cbb33c7..b022a177c4 100644
--- a/libpurple/protocols/jabber/auth_cyrus.c
+++ b/libpurple/protocols/jabber/auth_cyrus.c
@@ -36,7 +36,7 @@ static void disallow_plaintext_auth(PurpleAccount *account)
{
purple_connection_error_reason(purple_account_get_connection(account),
PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
- _("Server requires plaintext authentication over an unencrypted stream"));
+ _("Server may require plaintext authentication over an unencrypted stream"));
}
static void start_cyrus_wrapper(JabberStream *js)
@@ -94,7 +94,6 @@ static int jabber_sasl_cb_secret(sasl_conn_t *conn, void *ctx, int id, sasl_secr
PurpleAccount *account;
const char *pw;
size_t len;
- static sasl_secret_t *x = NULL;
account = purple_connection_get_account(js->gc);
pw = purple_account_get_password(account);
@@ -103,15 +102,16 @@ static int jabber_sasl_cb_secret(sasl_conn_t *conn, void *ctx, int id, sasl_secr
return SASL_BADPARAM;
len = strlen(pw);
- x = (sasl_secret_t *) realloc(x, sizeof(sasl_secret_t) + len);
-
- if (!x)
+ /* Not an off-by-one because sasl_secret_t defines char data[1] */
+ /* TODO: This can probably be moved to glib's allocator */
+ js->sasl_secret = malloc(sizeof(sasl_secret_t) + len);
+ if (!js->sasl_secret)
return SASL_NOMEM;
- x->len = len;
- strcpy((char*)x->data, pw);
+ js->sasl_secret->len = len;
+ strcpy((char*)js->sasl_secret->data, pw);
- *secret = x;
+ *secret = js->sasl_secret;
return SASL_OK;
}
@@ -176,7 +176,7 @@ auth_no_pass_cb(PurpleConnection *gc, PurpleRequestFields *fields)
account = purple_connection_get_account(gc);
js = purple_connection_get_protocol_data(gc);
- /* Disable the account as the user has canceled connecting */
+ /* Disable the account as the user has cancelled connecting */
purple_account_set_enabled(account, purple_core_get_ui(), FALSE);
}
@@ -240,8 +240,9 @@ jabber_auth_start_cyrus(JabberStream *js, xmlnode **reply, char **error)
* it in plaintext, see if we can turn on
* plaintext auth
*/
+ /* XXX Should we just check for PLAIN/LOGIN being offered mechanisms? */
} else if (!plaintext) {
- char *msg = g_strdup_printf(_("%s requires plaintext authentication over an unencrypted connection. Allow this and continue authentication?"),
+ char *msg = g_strdup_printf(_("%s may require plaintext authentication over an unencrypted connection. Allow this and continue authentication?"),
purple_account_get_username(account));
purple_request_yes_no(js->gc, _("Plaintext Authentication"),
_("Plaintext Authentication"),
@@ -252,12 +253,15 @@ jabber_auth_start_cyrus(JabberStream *js, xmlnode **reply, char **error)
g_free(msg);
return JABBER_SASL_STATE_CONTINUE;
- } else {
- /* We have no mechs which can work.
- * Try falling back on the old jabber:iq:auth method. We get here if the server supports
- * one or more sasl mechs, we are compiled with cyrus-sasl support, but we support or can connect with none of
- * the offerred mechs. jabberd 2.0 w/ SASL and Apple's iChat Server 10.5 both handle and expect
- * jabber:iq:auth in this situation. iChat Server in particular offers SASL GSSAPI by default, which is often
+ } else
+ js->auth_fail_count++;
+
+ if (js->auth_fail_count == 1 &&
+ (js->sasl_mechs->str && g_str_equal(js->sasl_mechs->str, "GSSAPI"))) {
+ /* If we tried GSSAPI first, it failed, and it was the only method we had to try, try jabber:iq:auth
+ * for compatibility with iChat 10.5 Server and other jabberd based servers.
+ *
+ * iChat Server 10.5 and certain other corporate servers offer SASL GSSAPI by default, which is often
* not configured on the client side, and expects a fallback to jabber:iq:auth when it (predictably) fails.
*
* Note: xep-0078 points out that using jabber:iq:auth after a sasl failure is wrong. However,
@@ -269,18 +273,21 @@ jabber_auth_start_cyrus(JabberStream *js, xmlnode **reply, char **error)
jabber_auth_start_old(js);
return JABBER_SASL_STATE_CONTINUE;
}
- /* not reached */
+
break;
/* Fatal errors. Give up and go home */
case SASL_BADPARAM:
case SASL_NOMEM:
+ *error = g_strdup(_("SASL authentication failed"));
break;
/* For everything else, fail the mechanism and try again */
default:
purple_debug_info("sasl", "sasl_state is %d, failing the mech and trying again\n", js->sasl_state);
+ js->auth_fail_count++;
+
/*
* DAA: is this right?
* The manpage says that "mech" will contain the chosen mechanism on success.
@@ -314,8 +321,11 @@ jabber_auth_start_cyrus(JabberStream *js, xmlnode **reply, char **error)
xmlnode_set_namespace(auth, NS_XMPP_SASL);
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");
+ if (g_str_equal(js->user->domain, "gmail.com") ||
+ g_str_equal(js->user->domain, "googlemail.com")) {
+ xmlnode_set_attrib(auth, "xmlns:ga", "http://www.google.com/talk/protocol/auth");
+ xmlnode_set_attrib(auth, "ga:client-uses-full-bind-result", "true");
+ }
if (clientout) {
if (coutlen == 0) {
@@ -330,7 +340,6 @@ jabber_auth_start_cyrus(JabberStream *js, xmlnode **reply, char **error)
*reply = auth;
return JABBER_SASL_STATE_CONTINUE;
} else {
- *error = g_strdup(_("SASL authentication failed"));
return JABBER_SASL_STATE_FAIL;
}
}
@@ -391,6 +400,7 @@ jabber_cyrus_start(JabberStream *js, xmlnode *mechanisms,
xmlnode **reply, char **error)
{
xmlnode *mechnode;
+ JabberSaslState ret;
js->sasl_mechs = g_string_new("");
@@ -399,18 +409,14 @@ jabber_cyrus_start(JabberStream *js, xmlnode *mechanisms,
{
char *mech_name = xmlnode_get_data(mechnode);
- if (!mech_name || !*mech_name) {
- g_free(mech_name);
- continue;
- }
-
- /* Don't include Google Talk's X-GOOGLE-TOKEN mechanism
- * or Facebook Chat's X-FACEBOOK-PLATFORM mechansim,
- * as we will not support them and including them gives a false fall-back
- * to other mechs offerred, leading to incorrect error handling.
+ /* Ignore blank mechanisms and EXTERNAL. External isn't
+ * supported, and Cyrus SASL's mechanism returns
+ * SASL_NOMECH when the caller (us) doesn't configure it.
+ * Except SASL_NOMECH is supposed to mean "no concordant
+ * mechanisms"... Easiest just to blacklist it (for now).
*/
- if (g_str_equal(mech_name, "X-GOOGLE-TOKEN")
- || g_str_equal(mech_name, "X-FACEBOOK-PLATFORM") ) {
+ if (!mech_name || !*mech_name ||
+ g_str_equal(mech_name, "EXTERNAL")) {
g_free(mech_name);
continue;
}
@@ -420,8 +426,21 @@ jabber_cyrus_start(JabberStream *js, xmlnode *mechanisms,
g_free(mech_name);
}
+ /* Strip off the trailing ' ' */
+ if (js->sasl_mechs->len > 1)
+ g_string_truncate(js->sasl_mechs, js->sasl_mechs->len - 1);
+
jabber_sasl_build_callbacks(js);
- return jabber_auth_start_cyrus(js, reply, error);
+ ret = jabber_auth_start_cyrus(js, reply, error);
+
+ /*
+ * Triggered if no overlap between server and client
+ * supported mechanisms.
+ */
+ if (ret == JABBER_SASL_STATE_FAIL && *error == NULL)
+ *error = g_strdup(_("Server does not use any supported authentication method"));
+
+ return ret;
}
static JabberSaslState
@@ -542,6 +561,25 @@ jabber_cyrus_handle_failure(JabberStream *js, xmlnode *packet,
sasl_dispose(&js->sasl);
return jabber_auth_start_cyrus(js, reply, error);
+
+ } else if ((js->auth_fail_count == 1) &&
+ (js->current_mech && g_str_equal(js->current_mech, "GSSAPI"))) {
+ /* If we tried GSSAPI first, it failed, and it was the only method we had to try, try jabber:iq:auth
+ * for compatibility with iChat 10.5 Server and other jabberd based servers.
+ *
+ * iChat Server 10.5 and certain other corporate servers offer SASL GSSAPI by default, which is often
+ * not configured on the client side, and expects a fallback to jabber:iq:auth when it (predictably) fails.
+ *
+ * Note: xep-0078 points out that using jabber:iq:auth after a sasl failure is wrong. However,
+ * I believe this refers to actual authentication failure, not a simple lack of concordant mechanisms.
+ * Doing otherwise means that simply compiling with SASL support renders the client unable to connect to servers
+ * which would connect without issue otherwise. -evands
+ */
+ sasl_dispose(&js->sasl);
+ js->sasl = NULL;
+ js->auth_mech = NULL;
+ jabber_auth_start_old(js);
+ return JABBER_SASL_STATE_CONTINUE;
}
}
diff --git a/libpurple/protocols/jabber/auth_digest_md5.c b/libpurple/protocols/jabber/auth_digest_md5.c
index 9de2f9cfff..0effe4a48f 100644
--- a/libpurple/protocols/jabber/auth_digest_md5.c
+++ b/libpurple/protocols/jabber/auth_digest_md5.c
@@ -188,16 +188,17 @@ digest_md5_handle_challenge(JabberStream *js, xmlnode *packet,
if (g_hash_table_lookup(parts, "rspauth")) {
char *rspauth = g_hash_table_lookup(parts, "rspauth");
+ char *expected_rspauth = js->auth_mech_data;
- if (rspauth && purple_strequal(rspauth, js->expected_rspauth)) {
+ if (rspauth && purple_strequal(rspauth, expected_rspauth)) {
reply = xmlnode_new("response");
xmlnode_set_namespace(reply, NS_XMPP_SASL);
} else {
*msg = g_strdup(_("Invalid challenge from server"));
state = JABBER_SASL_STATE_FAIL;
}
- g_free(js->expected_rspauth);
- js->expected_rspauth = NULL;
+ g_free(js->auth_mech_data);
+ js->auth_mech_data = NULL;
} else {
/* assemble a response, and send it */
/* see RFC 2831 */
@@ -235,7 +236,7 @@ digest_md5_handle_challenge(JabberStream *js, xmlnode *packet,
g_free(a2);
a2 = g_strdup_printf(":xmpp/%s", realm);
- js->expected_rspauth = generate_response_value(js->user,
+ js->auth_mech_data = generate_response_value(js->user,
purple_connection_get_password(js->gc), nonce, cnonce, a2, realm);
g_free(a2);
@@ -276,6 +277,13 @@ digest_md5_handle_challenge(JabberStream *js, xmlnode *packet,
return state;
}
+static void
+digest_md5_dispose(JabberStream *js)
+{
+ g_free(js->auth_mech_data);
+ js->auth_mech_data = NULL;
+}
+
static JabberSaslMech digest_md5_mech = {
10, /* priority */
"DIGEST-MD5", /* name */
@@ -283,7 +291,7 @@ static JabberSaslMech digest_md5_mech = {
digest_md5_handle_challenge,
NULL, /* handle_success */
NULL, /* handle_failure */
- NULL /* handle_dispose */
+ digest_md5_dispose,
};
JabberSaslMech *jabber_auth_get_digest_md5_mech(void)
diff --git a/libpurple/protocols/jabber/auth_plain.c b/libpurple/protocols/jabber/auth_plain.c
index 81d9118096..b05fba732f 100644
--- a/libpurple/protocols/jabber/auth_plain.c
+++ b/libpurple/protocols/jabber/auth_plain.c
@@ -40,13 +40,16 @@ static xmlnode *finish_plaintext_authentication(JabberStream *js)
auth = xmlnode_new("auth");
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");
+ if (g_str_equal(js->user->domain, "gmail.com") ||
+ g_str_equal(js->user->domain, "googlemail.com")) {
+ xmlnode_set_attrib(auth, "xmlns:ga", "http://www.google.com/talk/protocol/auth");
+ xmlnode_set_attrib(auth, "ga:client-uses-full-bind-result", "true");
+ }
response = g_string_new("");
- response = g_string_append_len(response, "\0", 1);
+ response = g_string_append_c(response, '\0');
response = g_string_append(response, js->user->node);
- response = g_string_append_len(response, "\0", 1);
+ response = g_string_append_c(response, '\0');
response = g_string_append(response,
purple_connection_get_password(js->gc));
diff --git a/libpurple/protocols/jabber/auth_scram.c b/libpurple/protocols/jabber/auth_scram.c
index de1822a944..6ef43314bf 100644
--- a/libpurple/protocols/jabber/auth_scram.c
+++ b/libpurple/protocols/jabber/auth_scram.c
@@ -47,6 +47,32 @@ static const JabberScramHash *mech_to_hash(const char *mech)
g_return_val_if_reached(NULL);
}
+static const struct {
+ const char *error;
+ const char *meaning;
+} server_errors[] = {
+ { "invalid-encoding",
+ N_("Invalid Encoding")},
+ { "extensions-not-supported",
+ N_("Unsupported Extension") },
+ { "channel-bindings-dont-match",
+ N_("Unexpected response from the server. This may indicate a possible MITM attack") },
+ { "server-does-support-channel-binding",
+ N_("The server does support channel binding, but did not appear to advertise it. This indicates a likely MITM attack") },
+ { "channel-binding-not-supported",
+ N_("Server does not support channel binding") },
+ { "unsupported-channel-binding-type",
+ N_("Unsupported channel binding method") },
+ { "unknown-user",
+ N_("User not found") },
+ { "invalid-username-encoding",
+ N_("Invalid Username Encoding") },
+ { "no-resources",
+ N_("Resource Constraint") },
+ { "other-error",
+ N_("Unknown Error") }
+};
+
guchar *jabber_scram_hi(const JabberScramHash *hash, const GString *str,
GString *salt, guint iterations)
{
@@ -301,11 +327,17 @@ jabber_scram_feed_parser(JabberScramData *data, gchar *in, gchar **out)
#endif
ret = jabber_scram_calc_proofs(data, salt, iterations);
- if (!ret)
+
+ g_string_free(salt, TRUE);
+ salt = NULL;
+ if (!ret) {
+ g_free(nonce);
return FALSE;
+ }
proof = purple_base64_encode((guchar *)data->client_proof->str, data->client_proof->len);
*out = g_strdup_printf("c=%s,r=%s,p=%s", "biws", nonce, proof);
+ g_free(nonce);
g_free(proof);
} else if (data->step == 2) {
gchar *server_sig, *enc_server_sig;
@@ -419,7 +451,7 @@ scram_handle_challenge(JabberStream *js, xmlnode *challenge, xmlnode **out, char
{
JabberScramData *data = js->auth_mech_data;
xmlnode *reply;
- gchar *enc_in, *dec_in;
+ gchar *enc_in, *dec_in = NULL;
gchar *enc_out = NULL, *dec_out = NULL;
gsize len;
JabberSaslState state = JABBER_SASL_STATE_FAIL;
@@ -434,7 +466,6 @@ scram_handle_challenge(JabberStream *js, xmlnode *challenge, xmlnode **out, char
}
dec_in = (gchar *)purple_base64_decode(enc_in, &len);
- g_free(enc_in);
if (!dec_in || len != strlen(dec_in)) {
/* Danger afoot; SCRAM shouldn't contain NUL bytes */
reply = xmlnode_new("abort");
@@ -468,6 +499,8 @@ scram_handle_challenge(JabberStream *js, xmlnode *challenge, xmlnode **out, char
state = JABBER_SASL_STATE_CONTINUE;
out:
+ g_free(enc_in);
+ g_free(dec_in);
g_free(enc_out);
g_free(dec_out);
@@ -484,13 +517,24 @@ scram_handle_success(JabberStream *js, xmlnode *packet, char **error)
gsize len;
enc_in = xmlnode_get_data(packet);
- g_return_val_if_fail(enc_in != NULL && *enc_in != '\0', FALSE);
+ if (data->step != 3 && (!enc_in || *enc_in == '\0')) {
+ *error = g_strdup(_("Invalid challenge from server"));
+ g_free(enc_in);
+ return JABBER_SASL_STATE_FAIL;
+ }
- if (data->step == 3)
+ if (data->step == 3) {
+ /*
+ * If the server took the slow approach (sending the verifier
+ * as a challenge/response pair), we get here.
+ */
+ g_free(enc_in);
return JABBER_SASL_STATE_OK;
+ }
if (data->step != 2) {
*error = g_strdup(_("Unexpected response from server"));
+ g_free(enc_in);
return JABBER_SASL_STATE_FAIL;
}
@@ -499,18 +543,20 @@ scram_handle_success(JabberStream *js, xmlnode *packet, char **error)
if (!dec_in || len != strlen(dec_in)) {
/* Danger afoot; SCRAM shouldn't contain NUL bytes */
g_free(dec_in);
- *error = g_strdup(_("Invalid challenge from server"));
+ *error = g_strdup(_("Malicious challenge from server"));
return JABBER_SASL_STATE_FAIL;
}
purple_debug_misc("jabber", "decoded success: %s\n", dec_in);
if (!jabber_scram_feed_parser(data, dec_in, &dec_out) || dec_out != NULL) {
+ g_free(dec_in);
g_free(dec_out);
*error = g_strdup(_("Invalid challenge from server"));
return JABBER_SASL_STATE_FAIL;
}
+ g_free(dec_in);
/* Hooray */
return JABBER_SASL_STATE_OK;
}
diff --git a/libpurple/protocols/jabber/bosh.c b/libpurple/protocols/jabber/bosh.c
index b19fc68ac2..f0116a5418 100644
--- a/libpurple/protocols/jabber/bosh.c
+++ b/libpurple/protocols/jabber/bosh.c
@@ -78,13 +78,11 @@ struct _PurpleBOSHConnection {
} state;
guint8 failed_connections;
- int max_inactivity;
int wait;
int max_requests;
int requests;
- guint inactivity_timer;
guint send_timer;
};
@@ -197,6 +195,11 @@ jabber_bosh_connection_init(JabberStream *js, const char *url)
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");
@@ -239,8 +242,6 @@ jabber_bosh_connection_destroy(PurpleBOSHConnection *conn)
if (conn->send_timer)
purple_timeout_remove(conn->send_timer);
- if (conn->inactivity_timer)
- purple_timeout_remove(conn->inactivity_timer);
purple_circ_buffer_destroy(conn->pending);
@@ -433,34 +434,14 @@ send_timer_cb(gpointer data)
return FALSE;
}
-static gboolean
-bosh_inactivity_cb(gpointer data)
+void
+jabber_bosh_connection_send_keepalive(PurpleBOSHConnection *bosh)
{
- PurpleBOSHConnection *bosh = data;
- bosh->inactivity_timer = 0;
-
if (bosh->send_timer != 0)
purple_timeout_remove(bosh->send_timer);
/* clears bosh->send_timer */
send_timer_cb(bosh);
-
- return FALSE;
-}
-
-static void
-restart_inactivity_timer(PurpleBOSHConnection *conn)
-{
- if (conn->inactivity_timer != 0) {
- purple_timeout_remove(conn->inactivity_timer);
- conn->inactivity_timer = 0;
- }
-
- if (conn->max_inactivity != 0) {
- conn->inactivity_timer =
- purple_timeout_add_seconds(conn->max_inactivity - 5 /* rounding */,
- bosh_inactivity_cb, conn);
- }
}
static void jabber_bosh_connection_received(PurpleBOSHConnection *conn, xmlnode *node) {
@@ -541,19 +522,20 @@ static void boot_response_cb(PurpleBOSHConnection *conn, xmlnode *node) {
}
if (inactivity) {
- conn->max_inactivity = atoi(inactivity);
- if (conn->max_inactivity <= 5) {
+ js->max_inactivity = atoi(inactivity);
+ if (js->max_inactivity <= 5) {
purple_debug_warning("jabber", "Ignoring bogusly small inactivity: %s\n",
inactivity);
- conn->max_inactivity = 0;
+ /* Leave it at the default */
} else {
- /* TODO: Integrate this with jabber.c keepalive checks... */
/* TODO: Can this check fail? It shouldn't */
- if (conn->inactivity_timer == 0) {
+ 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",
- conn->max_inactivity - 5);
- restart_inactivity_timer(conn);
+ js->max_inactivity);
+ jabber_stream_restart_inactivity_timer(js);
}
}
}
@@ -729,11 +711,10 @@ jabber_bosh_http_connection_process(PurpleHTTPConnection *conn)
/* Make sure Content-Length is in headers, not body */
if (content_length && (!end_of_headers || content_length < end_of_headers)) {
const char *sep;
- const char *eol;
int len;
if ((sep = strstr(content_length, ": ")) == NULL ||
- (eol = strstr(sep, "\r\n")) == NULL)
+ strstr(sep, "\r\n") == NULL)
/*
* The packet ends in the middle of the Content-Length line.
* We'll try again later when we have more.
@@ -976,7 +957,7 @@ http_connection_send_request(PurpleHTTPConnection *conn, const GString *req)
size_t len;
/* Sending something to the server, restart the inactivity timer */
- restart_inactivity_timer(conn->bosh);
+ jabber_stream_restart_inactivity_timer(conn->bosh->js);
data = g_strdup_printf("POST %s HTTP/1.1\r\n"
"Host: %s\r\n"
diff --git a/libpurple/protocols/jabber/bosh.h b/libpurple/protocols/jabber/bosh.h
index 1f1d2e7d63..0648ec0b8f 100644
--- a/libpurple/protocols/jabber/bosh.h
+++ b/libpurple/protocols/jabber/bosh.h
@@ -35,6 +35,7 @@ PurpleBOSHConnection* jabber_bosh_connection_init(JabberStream *js, const char *
void jabber_bosh_connection_destroy(PurpleBOSHConnection *conn);
gboolean jabber_bosh_connection_is_ssl(PurpleBOSHConnection *conn);
+void jabber_bosh_connection_send_keepalive(PurpleBOSHConnection *conn);
void jabber_bosh_connection_connect(PurpleBOSHConnection *conn);
void jabber_bosh_connection_close(PurpleBOSHConnection *conn);
diff --git a/libpurple/protocols/jabber/caps.c b/libpurple/protocols/jabber/caps.c
index 60a06130eb..d291634551 100644
--- a/libpurple/protocols/jabber/caps.c
+++ b/libpurple/protocols/jabber/caps.c
@@ -29,6 +29,7 @@
#include "iq.h"
#include "presence.h"
#include "util.h"
+#include "xdata.h"
#define JABBER_CAPS_FILENAME "xmpp-caps.xml"
@@ -41,20 +42,14 @@ static GHashTable *capstable = NULL; /* JabberCapsTuple -> JabberCapsClientInfo
static GHashTable *nodetable = NULL; /* char *node -> JabberCapsNodeExts */
static guint save_timer = 0;
-/**
- * Processes a query-node and returns a JabberCapsClientInfo object with all relevant info.
- *
- * @param query A query object.
- * @return A JabberCapsClientInfo object.
- */
-static JabberCapsClientInfo *jabber_caps_parse_client_info(xmlnode *query);
-
/* Free a GList of allocated char* */
static void
free_string_glist(GList *list)
{
- g_list_foreach(list, (GFunc)g_free, NULL);
- g_list_free(list);
+ while (list) {
+ g_free(list->data);
+ list = g_list_delete_link(list, list);
+ }
}
static JabberCapsNodeExts*
@@ -238,15 +233,15 @@ jabber_caps_load(void)
if(!capsdata)
return;
- if (strcmp(capsdata->name, "capabilities") != 0) {
+ if (!g_str_equal(capsdata->name, "capabilities")) {
xmlnode_free(capsdata);
return;
}
- for(client = capsdata->child; client; client = client->next) {
- if(client->type != XMLNODE_TYPE_TAG)
+ for (client = capsdata->child; client; client = client->next) {
+ if (client->type != XMLNODE_TYPE_TAG)
continue;
- if(!strcmp(client->name, "client")) {
+ if (g_str_equal(client->name, "client")) {
JabberCapsClientInfo *value = g_new0(JabberCapsClientInfo, 1);
JabberCapsTuple *key = (JabberCapsTuple*)&value->tuple;
xmlnode *child;
@@ -259,15 +254,15 @@ jabber_caps_load(void)
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)
+ for (child = client->child; child; child = child->next) {
+ if (child->type != XMLNODE_TYPE_TAG)
continue;
- if(!strcmp(child->name,"feature")) {
+ if (g_str_equal(child->name, "feature")) {
const char *var = xmlnode_get_attrib(child, "var");
if(!var)
continue;
value->features = g_list_append(value->features,g_strdup(var));
- } else if(!strcmp(child->name,"identity")) {
+ } 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");
@@ -284,15 +279,15 @@ jabber_caps_load(void)
id->lang = g_strdup(lang);
value->identities = g_list_append(value->identities,id);
- } else if(!strcmp(child->name,"x")) {
+ } else if (g_str_equal(child->name, "x")) {
/* 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));
- } else if (!strcmp(child->name, "ext") && key->hash != NULL) {
+ } else if (g_str_equal(child->name, "ext") && key->hash != NULL) {
purple_debug_warning("jabber", "Ignoring exts when reading new-style caps\n");
- } else if (!strcmp(child->name, "ext")) {
+ } else if (g_str_equal(child->name, "ext")) {
/* TODO: Do we care about reading in the identities listed here? */
const char *identifier = xmlnode_get_attrib(child, "identifier");
xmlnode *node;
@@ -304,7 +299,7 @@ jabber_caps_load(void)
for (node = child->child; node; node = node->next) {
if (node->type != XMLNODE_TYPE_TAG)
continue;
- if (!strcmp(node->name, "feature")) {
+ if (g_str_equal(node->name, "feature")) {
const char *var = xmlnode_get_attrib(node, "var");
if (!var)
continue;
@@ -463,13 +458,13 @@ jabber_caps_client_iqcb(JabberStream *js, const char *from, JabberIqType type,
* size in jabber_caps_calculate_hash is large enough. The cipher API
* doesn't seem to offer a "Get the hash size" function(?).
*/
- if (!strcmp(userdata->hash, "sha-1")) {
+ if (g_str_equal(userdata->hash, "sha-1")) {
hash = jabber_caps_calculate_hash(info, "sha1");
- } else if (!strcmp(userdata->hash, "md5")) {
+ } else if (g_str_equal(userdata->hash, "md5")) {
hash = jabber_caps_calculate_hash(info, "md5");
}
- if (!hash || strcmp(hash, userdata->ver)) {
+ 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"),
@@ -709,44 +704,6 @@ void jabber_caps_get_info(JabberStream *js, const char *who, const char *node,
}
static gint
-jabber_identity_compare(gconstpointer a, gconstpointer b)
-{
- const JabberIdentity *ac;
- const JabberIdentity *bc;
- gint cat_cmp;
- gint typ_cmp;
-
- ac = a;
- bc = b;
-
- if ((cat_cmp = strcmp(ac->category, bc->category)) == 0) {
- if ((typ_cmp = strcmp(ac->type, bc->type)) == 0) {
- if (!ac->lang && !bc->lang) {
- return 0;
- } else if (ac->lang && !bc->lang) {
- return 1;
- } else if (!ac->lang && bc->lang) {
- return -1;
- } else {
- return strcmp(ac->lang, bc->lang);
- }
- } else {
- return typ_cmp;
- }
- } else {
- return cat_cmp;
- }
-}
-
-static gchar *jabber_caps_get_formtype(const xmlnode *x) {
- xmlnode *formtypefield;
- formtypefield = xmlnode_get_child(x, "field");
- while (formtypefield && strcmp(xmlnode_get_attrib(formtypefield, "var"), "FORM_TYPE")) formtypefield = xmlnode_get_next_twin(formtypefield);
- formtypefield = xmlnode_get_child(formtypefield, "value");
- return xmlnode_get_data(formtypefield);;
-}
-
-static gint
jabber_xdata_compare(gconstpointer a, gconstpointer b)
{
const xmlnode *aformtypefield = a;
@@ -755,8 +712,8 @@ jabber_xdata_compare(gconstpointer a, gconstpointer b)
char *bformtype;
int result;
- aformtype = jabber_caps_get_formtype(aformtypefield);
- bformtype = jabber_caps_get_formtype(bformtypefield);
+ aformtype = jabber_x_data_get_formtype(aformtypefield);
+ bformtype = jabber_x_data_get_formtype(bformtypefield);
result = strcmp(aformtype, bformtype);
g_free(aformtype);
@@ -764,20 +721,21 @@ jabber_xdata_compare(gconstpointer a, gconstpointer b)
return result;
}
-static JabberCapsClientInfo *jabber_caps_parse_client_info(xmlnode *query)
+JabberCapsClientInfo *jabber_caps_parse_client_info(xmlnode *query)
{
xmlnode *child;
JabberCapsClientInfo *info;
- if (!query || strcmp(query->xmlns, NS_DISCO_INFO))
- return 0;
+ if (!query || !g_str_equal(query->name, "query") ||
+ !purple_strequal(query->xmlns, NS_DISCO_INFO))
+ return NULL;
info = g_new0(JabberCapsClientInfo, 1);
for(child = query->child; child; child = child->next) {
if (child->type != XMLNODE_TYPE_TAG)
continue;
- if (!strcmp(child->name,"identity")) {
+ 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");
@@ -795,13 +753,13 @@ static JabberCapsClientInfo *jabber_caps_parse_client_info(xmlnode *query)
id->lang = g_strdup(lang);
info->identities = g_list_append(info->identities, id);
- } else if (!strcmp(child->name, "feature")) {
+ } else if (g_str_equal(child->name, "feature")) {
/* parse feature */
const char *var = xmlnode_get_attrib(child, "var");
if (var)
info->features = g_list_prepend(info->features, g_strdup(var));
- } else if (!strcmp(child->name, "x")) {
- if (child->xmlns && !strcmp(child->xmlns, "jabber:x:data")) {
+ } else if (g_str_equal(child->name, "x")) {
+ if (purple_strequal(child->xmlns, "jabber:x:data")) {
/* x-data form */
xmlnode *dataform = xmlnode_copy(child);
info->forms = g_list_append(info->forms, dataform);
@@ -848,9 +806,12 @@ static GList* jabber_caps_xdata_get_fields(const xmlnode *x)
static void
append_escaped_string(PurpleCipherContext *context, const gchar *str)
{
- char *tmp = g_markup_escape_text(str, -1);
- purple_cipher_context_append(context, (const guchar *)tmp, strlen(tmp));
- g_free(tmp);
+ if (str && *str) {
+ char *tmp = g_markup_escape_text(str, -1);
+ purple_cipher_context_append(context, (const guchar *)tmp, strlen(tmp));
+ g_free(tmp);
+ }
+
purple_cipher_context_append(context, (const guchar *)"<", 1);
}
@@ -904,7 +865,7 @@ gchar *jabber_caps_calculate_hash(JabberCapsClientInfo *info, const char *hash)
/* concat x-data forms to the verification string */
for(node = info->forms; node; node = node->next) {
xmlnode *data = (xmlnode *)node->data;
- gchar *formtype = jabber_caps_get_formtype(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 */
@@ -965,6 +926,10 @@ void jabber_caps_calculate_own_hash(JabberStream *js) {
}
info.features = features;
+ /* TODO: This copy can go away, I think, since jabber_identities
+ * is pre-sorted, so the sort in calculate_hash should be idempotent.
+ * However, I want to test that. --darkrain
+ */
info.identities = g_list_copy(jabber_identities);
info.forms = NULL;
@@ -989,7 +954,7 @@ void jabber_caps_broadcast_change()
for (node = accounts; node; node = node->next) {
PurpleAccount *account = node->data;
const char *prpl_id = purple_account_get_protocol_id(account);
- if (!strcmp("prpl-jabber", prpl_id) && purple_account_is_connected(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);
}
diff --git a/libpurple/protocols/jabber/caps.h b/libpurple/protocols/jabber/caps.h
index 46a6dd770e..897754f6d9 100644
--- a/libpurple/protocols/jabber/caps.h
+++ b/libpurple/protocols/jabber/caps.h
@@ -115,4 +115,15 @@ const gchar* jabber_caps_get_own_hash(JabberStream *js);
*/
void jabber_caps_broadcast_change(void);
+/**
+ * Parse the <query/> element from an IQ stanza into a JabberCapsClientInfo
+ * struct.
+ *
+ * Exposed for tests
+ *
+ * @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);
+
#endif /* PURPLE_JABBER_CAPS_H_ */
diff --git a/libpurple/protocols/jabber/chat.c b/libpurple/protocols/jabber/chat.c
index 6eefd9e404..380d5634a5 100644
--- a/libpurple/protocols/jabber/chat.c
+++ b/libpurple/protocols/jabber/chat.c
@@ -173,8 +173,10 @@ void jabber_chat_invite(PurpleConnection *gc, int id, const char *msg,
xmlnode_set_namespace(x, "http://jabber.org/protocol/muc#user");
invite = xmlnode_new_child(x, "invite");
xmlnode_set_attrib(invite, "to", name);
- body = xmlnode_new_child(invite, "reason");
- xmlnode_insert_data(body, msg, -1);
+ if (msg) {
+ body = xmlnode_new_child(invite, "reason");
+ xmlnode_insert_data(body, msg, -1);
+ }
} else {
xmlnode_set_attrib(message, "to", name);
/*
@@ -184,14 +186,17 @@ void jabber_chat_invite(PurpleConnection *gc, int id, const char *msg,
*
* Left here for compatibility.
*/
- body = xmlnode_new_child(message, "body");
- xmlnode_insert_data(body, msg, -1);
+ if (msg) {
+ body = xmlnode_new_child(message, "body");
+ xmlnode_insert_data(body, msg, -1);
+ }
x = xmlnode_new_child(message, "x");
xmlnode_set_attrib(x, "jid", room_jid);
/* The better place for it! XEP-0249 style. */
- xmlnode_set_attrib(x, "reason", msg);
+ if (msg)
+ xmlnode_set_attrib(x, "reason", msg);
xmlnode_set_namespace(x, "jabber:x:conference");
}
diff --git a/libpurple/protocols/jabber/chat.h b/libpurple/protocols/jabber/chat.h
index 8beae73ad8..a7717eb7ef 100644
--- a/libpurple/protocols/jabber/chat.h
+++ b/libpurple/protocols/jabber/chat.h
@@ -102,8 +102,6 @@ gboolean jabber_chat_affiliation_list(JabberChat *chat, const char *affiliation)
gboolean jabber_chat_role_user(JabberChat *chat, const char *who,
const char *role, const char *why);
gboolean jabber_chat_role_list(JabberChat *chat, const char *role);
-gboolean jabber_chat_kick_user(JabberChat *chat, const char *who,
- const char *why);
PurpleRoomlist *jabber_roomlist_get_list(PurpleConnection *gc);
void jabber_roomlist_cancel(PurpleRoomlist *list);
diff --git a/libpurple/protocols/jabber/data.c b/libpurple/protocols/jabber/data.c
index b6add42e9d..1708946a8b 100644
--- a/libpurple/protocols/jabber/data.c
+++ b/libpurple/protocols/jabber/data.c
@@ -1,4 +1,6 @@
/*
+ * purple - Jabber Service Discovery
+ *
* 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.
@@ -11,11 +13,12 @@
* 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.
+ * 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"
@@ -36,11 +39,18 @@ static GHashTable *remote_data_by_cid = NULL;
JabberData *
jabber_data_create_from_data(gconstpointer rawdata, gsize size, const char *type,
- JabberStream *js)
+ gboolean ephemeral, JabberStream *js)
{
- JabberData *data = g_new0(JabberData, 1);
- gchar *checksum = jabber_calculate_data_hash(rawdata, size, "sha1");
- gchar cid[256];
+ JabberData *data;
+ gchar *checksum;
+ gchar cid[256]; /* "Big enough" for a SHA1 hex hash value */
+
+ g_return_val_if_fail(rawdata != NULL, NULL);
+ g_return_val_if_fail(size > 0, NULL);
+ g_return_val_if_fail(type != NULL, NULL);
+
+ data = g_new0(JabberData, 1);
+ checksum = jabber_calculate_data_hash(rawdata, size, "sha1");
g_snprintf(cid, sizeof(cid), "sha1+%s@bob.xmpp.org", checksum);
g_free(checksum);
@@ -48,6 +58,7 @@ jabber_data_create_from_data(gconstpointer rawdata, gsize size, const char *type
data->cid = g_strdup(cid);
data->type = g_strdup(type);
data->size = size;
+ data->ephemeral = ephemeral;
data->data = g_memdup(rawdata, size);
@@ -58,6 +69,7 @@ static void
jabber_data_delete(gpointer cbdata)
{
JabberData *data = cbdata;
+
g_free(data->cid);
g_free(data->type);
g_free(data->data);
@@ -72,6 +84,8 @@ jabber_data_create_from_xml(xmlnode *tag)
gchar *raw_data = NULL;
const gchar *cid, *type;
+ g_return_val_if_fail(tag != NULL, NULL);
+
/* check if this is a "data" tag */
if (strcmp(tag->name, "data") != 0) {
purple_debug_error("jabber", "Invalid data element\n");
@@ -110,9 +124,19 @@ jabber_data_create_from_xml(xmlnode *tag)
return data;
}
+void
+jabber_data_destroy(JabberData *data)
+{
+ g_return_if_fail(data != NULL);
+
+ jabber_data_delete(data);
+}
+
const char *
jabber_data_get_cid(const JabberData *data)
{
+ g_return_val_if_fail(data != NULL, NULL);
+
return data->cid;
}
@@ -120,26 +144,37 @@ jabber_data_get_cid(const JabberData *data)
const char *
jabber_data_get_type(const JabberData *data)
{
+ g_return_val_if_fail(data != NULL, NULL);
+
return data->type;
}
gsize
jabber_data_get_size(const JabberData *data)
{
+ g_return_val_if_fail(data != NULL, 0);
+
return data->size;
}
gpointer
jabber_data_get_data(const JabberData *data)
{
+ g_return_val_if_fail(data != NULL, NULL);
+
return data->data;
}
xmlnode *
jabber_data_get_xml_definition(const JabberData *data)
{
- xmlnode *tag = xmlnode_new("data");
- char *base64data = purple_base64_encode(data->data, data->size);
+ xmlnode *tag;
+ char *base64data;
+
+ g_return_val_if_fail(data != NULL, NULL);
+
+ tag = xmlnode_new("data");
+ base64data = purple_base64_encode(data->data, data->size);
xmlnode_set_namespace(tag, NS_BOB);
xmlnode_set_attrib(tag, "cid", data->cid);
@@ -155,12 +190,18 @@ jabber_data_get_xml_definition(const JabberData *data)
xmlnode *
jabber_data_get_xhtml_im(const JabberData *data, const gchar *alt)
{
- xmlnode *img = xmlnode_new("img");
- char src[128];
+ xmlnode *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);
- g_snprintf(src, sizeof(src), "cid:%s", data->cid);
+
+ src = g_strconcat("cid:", data->cid, NULL);
xmlnode_set_attrib(img, "src", src);
+ g_free(src);
return img;
}
@@ -250,7 +291,7 @@ jabber_data_request_cb(JabberStream *js, const char *from,
if (data_element && type == JABBER_IQ_RESULT) {
JabberData *data = jabber_data_create_from_xml(data_element);
- if (!ephemeral) {
+ if (data && !ephemeral) {
jabber_data_associate_remote(js, from, data);
}
cb(data, alt, userdata);
@@ -259,7 +300,7 @@ jabber_data_request_cb(JabberStream *js, const char *from,
"Responder didn't recognize requested data\n");
cb(NULL, alt, userdata);
} else {
- purple_debug_error("jabber", "Unknown response to data request\n");
+ purple_debug_warning("jabber", "Unknown response to data request\n");
cb(NULL, alt, userdata);
}
@@ -271,9 +312,17 @@ jabber_data_request(JabberStream *js, const gchar *cid, const gchar *who,
gchar *alt, gboolean ephemeral, JabberDataRequestCallback cb,
gpointer userdata)
{
- JabberIq *request = jabber_iq_new(js, JABBER_IQ_GET);
- xmlnode *data_request = jabber_data_get_xml_request(cid);
- JabberDataRequestData *data = g_new0(JabberDataRequestData, 1);
+ JabberIq *request;
+ xmlnode *data_request;
+ JabberDataRequestData *data;
+
+ g_return_if_fail(cid != NULL);
+ g_return_if_fail(who != NULL);
+ g_return_if_fail(alt != NULL);
+
+ request = jabber_iq_new(js, JABBER_IQ_GET);
+ data_request = jabber_data_get_xml_request(cid);
+ data = g_new0(JabberDataRequestData, 1);
data->userdata = userdata;
data->alt = alt;
@@ -289,14 +338,14 @@ jabber_data_request(JabberStream *js, const gchar *cid, const gchar *who,
const JabberData *
jabber_data_find_local_by_alt(const gchar *alt)
{
- purple_debug_info("jabber", "looking up local smiley with alt = %s\n", alt);
+ purple_debug_info("jabber", "looking up local data object with alt = %s\n", alt);
return g_hash_table_lookup(local_data_by_alt, alt);
}
const JabberData *
jabber_data_find_local_by_cid(const gchar *cid)
{
- purple_debug_info("jabber", "lookup local smiley with cid = %s\n", cid);
+ purple_debug_info("jabber", "lookup local data object with cid = %s\n", cid);
return g_hash_table_lookup(local_data_by_cid, cid);
}
@@ -305,7 +354,7 @@ jabber_data_find_remote_by_cid(JabberStream *js, const gchar *who,
const gchar *cid)
{
const JabberData *data = g_hash_table_lookup(remote_data_by_cid, cid);
- purple_debug_info("jabber", "lookup remote smiley with cid = %s\n", cid);
+ purple_debug_info("jabber", "lookup remote data object with cid = %s\n", cid);
if (data == NULL) {
gchar *jid_cid =
@@ -323,9 +372,12 @@ jabber_data_find_remote_by_cid(JabberStream *js, const gchar *who,
void
jabber_data_associate_local(JabberData *data, const gchar *alt)
{
- purple_debug_info("jabber", "associating local smiley\n alt = %s, cid = %s\n",
- alt, jabber_data_get_cid(data));
- g_hash_table_insert(local_data_by_alt, g_strdup(alt), data);
+ g_return_if_fail(data != NULL);
+
+ purple_debug_info("jabber", "associating local data object\n alt = %s, cid = %s\n",
+ alt , jabber_data_get_cid(data));
+ if (alt)
+ g_hash_table_insert(local_data_by_alt, g_strdup(alt), data);
g_hash_table_insert(local_data_by_cid, g_strdup(jabber_data_get_cid(data)),
data);
}
@@ -334,7 +386,9 @@ void
jabber_data_associate_remote(JabberStream *js, const gchar *who, JabberData *data)
{
gchar *cid;
-
+
+ g_return_if_fail(data != NULL);
+
if (jabber_data_has_valid_hash(data)) {
cid = g_strdup(jabber_data_get_cid(data));
} else {
@@ -371,6 +425,11 @@ jabber_data_parse(JabberStream *js, const char *who, JabberIqType type,
xmlnode_set_attrib(result->node, "id", id);
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 */
+ if (data->ephemeral) {
+ g_hash_table_remove(local_data_by_cid, cid);
+ }
}
jabber_iq_send(result);
}
diff --git a/libpurple/protocols/jabber/data.h b/libpurple/protocols/jabber/data.h
index 6036b6aceb..c0be150c32 100644
--- a/libpurple/protocols/jabber/data.h
+++ b/libpurple/protocols/jabber/data.h
@@ -1,4 +1,6 @@
/*
+ * purple - Jabber Service Discovery
+ *
* 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.
@@ -11,11 +13,12 @@
* 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.
+ * 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_DATA_H
@@ -34,6 +37,7 @@ typedef struct {
char *type;
gsize size;
gpointer data;
+ gboolean ephemeral;
} JabberData;
typedef void (JabberDataRequestCallback)(JabberData *data, gchar *alt,
@@ -42,12 +46,16 @@ typedef void (JabberDataRequestCallback)(JabberData *data, gchar *alt,
/* creates a JabberData instance from raw data */
JabberData *jabber_data_create_from_data(gconstpointer data, gsize size,
- const char *type, JabberStream *js);
+ const char *type, gboolean ephemeral, JabberStream *js);
/* create a JabberData instance from an XML "data" element (as defined by
XEP 0231 */
JabberData *jabber_data_create_from_xml(xmlnode *tag);
+/* destroy a JabberData instance, NOT to be used on data that has been
+ associated, since they get "owned" */
+void jabber_data_destroy(JabberData *data);
+
const char *jabber_data_get_cid(const JabberData *data);
const char *jabber_data_get_type(const JabberData *data);
diff --git a/libpurple/protocols/jabber/disco.c b/libpurple/protocols/jabber/disco.c
index 03ef6f02df..5803ca7d2d 100644
--- a/libpurple/protocols/jabber/disco.c
+++ b/libpurple/protocols/jabber/disco.c
@@ -606,7 +606,7 @@ jabber_disco_server_items_result_cb(JabberStream *js, const char *from,
/* we don't actually care about the specific nodes,
* so we won't query them */
- if((node = xmlnode_get_attrib(child, "node")))
+ if(xmlnode_get_attrib(child, "node") != NULL)
continue;
iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_DISCO_INFO);
diff --git a/libpurple/protocols/jabber/ibb.c b/libpurple/protocols/jabber/ibb.c
index 4283beb93f..11d9b3820d 100644
--- a/libpurple/protocols/jabber/ibb.c
+++ b/libpurple/protocols/jabber/ibb.c
@@ -1,4 +1,6 @@
/*
+ * purple - Jabber Service Discovery
+ *
* 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.
@@ -11,11 +13,12 @@
* 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.
+ * 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"
diff --git a/libpurple/protocols/jabber/ibb.h b/libpurple/protocols/jabber/ibb.h
index 633a71cc84..224ef938b0 100644
--- a/libpurple/protocols/jabber/ibb.h
+++ b/libpurple/protocols/jabber/ibb.h
@@ -1,4 +1,6 @@
/*
+ * purple - Jabber Service Discovery
+ *
* 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.
@@ -11,11 +13,12 @@
* 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.
+ * 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_IBB_H_
diff --git a/libpurple/protocols/jabber/jabber.c b/libpurple/protocols/jabber/jabber.c
index 45dca17186..1b487c1312 100644
--- a/libpurple/protocols/jabber/jabber.c
+++ b/libpurple/protocols/jabber/jabber.c
@@ -73,6 +73,10 @@
#include "jingle/rtp.h"
#define PING_TIMEOUT 60
+/* Send a whitespace keepalive to the server if we haven't sent
+ * anything in the last 120 seconds
+ */
+#define DEFAULT_INACTIVITY_TIME 120
GList *jabber_features = NULL;
GList *jabber_identities = NULL;
@@ -168,6 +172,8 @@ static void jabber_bind_result_cb(JabberStream *js, const char *from,
char *msg = jabber_parse_error(js, packet, &reason);
purple_connection_error_reason(js->gc, reason, msg);
g_free(msg);
+
+ return;
}
jabber_session_init(js);
@@ -201,19 +207,55 @@ static char *jabber_prep_resource(char *input) {
* resource string from being unreasonably long on systems which stuff the
* whole FQDN in the hostname */
if((dot = strchr(hostname, '.')))
- dot = '\0';
+ *dot = '\0';
return purple_strreplace(input, "__HOSTNAME__", hostname);
}
+static gboolean
+jabber_process_starttls(JabberStream *js, xmlnode *packet)
+{
+ PurpleAccount *account;
+ xmlnode *starttls;
+
+ account = purple_connection_get_account(js->gc);
+
+ if(purple_ssl_is_supported()) {
+ jabber_send_raw(js,
+ "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>", -1);
+ return TRUE;
+ }
+
+ starttls = xmlnode_get_child(packet, "starttls");
+ if(xmlnode_get_child(starttls, "required")) {
+ purple_connection_error_reason(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_NO_SSL_SUPPORT,
+ _("You require encryption, but no TLS/SSL support was found."));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
void jabber_stream_features_parse(JabberStream *js, xmlnode *packet)
{
- if(xmlnode_get_child(packet, "starttls")) {
- if(jabber_process_starttls(js, 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 (jabber_process_starttls(js, packet)) {
jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING_ENCRYPTION);
return;
}
- } else if(purple_account_get_bool(js->gc->account, "require_tls", JABBER_DEFAULT_REQUIRE_TLS) && !jabber_stream_is_ssl(js)) {
+ } else if (g_str_equal(connection_security, "require_tls") && !jabber_stream_is_ssl(js)) {
purple_connection_error_reason(js->gc,
PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
_("You require encryption, but it is not available on this server."));
@@ -360,8 +402,10 @@ static gboolean do_jabber_send_raw(JabberStream *js, const char *data, int len)
int ret;
gboolean success = TRUE;
- if (len == -1)
- len = strlen(data);
+ g_return_val_if_fail(len > 0, FALSE);
+
+ if (js->state == JABBER_STREAM_CONNECTED)
+ jabber_stream_restart_inactivity_timer(js);
if (js->writeh == 0)
ret = jabber_do_send(js, data, len);
@@ -402,6 +446,12 @@ static gboolean do_jabber_send_raw(JabberStream *js, const char *data, int len)
void jabber_send_raw(JabberStream *js, const char *data, int len)
{
+ PurpleConnection *gc;
+ PurpleAccount *account;
+
+ gc = js->gc;
+ account = purple_connection_get_account(gc);
+
/* because printing a tab to debug every minute gets old */
if(strcmp(data, "\t")) {
const char *username;
@@ -429,9 +479,9 @@ void jabber_send_raw(JabberStream *js, const char *data, int len)
*data_start = '\0';
}
- username = purple_connection_get_display_name(js->gc);
+ username = purple_connection_get_display_name(gc);
if (!username)
- username = purple_account_get_username(purple_connection_get_account(js->gc));
+ username = purple_account_get_username(account);
purple_debug_misc("jabber", "Sending%s (%s): %s%s%s\n",
jabber_stream_is_ssl(js) ? " (ssl)" : "", username,
@@ -442,10 +492,13 @@ void jabber_send_raw(JabberStream *js, const char *data, int len)
g_free(text);
}
- purple_signal_emit(purple_connection_get_prpl(js->gc), "jabber-sending-text", js->gc, &data);
+ purple_signal_emit(purple_connection_get_prpl(gc), "jabber-sending-text", gc, &data);
if (data == NULL)
return;
+ if (len == -1)
+ len = strlen(data);
+
/* If we've got a security layer, we need to encode the data,
* splitting it on the maximum buffer length negotiated */
#ifdef HAVE_CYRUS_SASL
@@ -453,21 +506,36 @@ void jabber_send_raw(JabberStream *js, const char *data, int len)
int pos = 0;
if (!js->gsc && js->fd<0)
- return;
-
- if (len == -1)
- len = strlen(data);
+ g_return_if_reached();
while (pos < len) {
int towrite;
const char *out;
unsigned olen;
+ int rc;
towrite = MIN((len - pos), js->sasl_maxbuf);
- sasl_encode(js->sasl, &data[pos], towrite, &out, &olen);
+ rc = sasl_encode(js->sasl, &data[pos], towrite,
+ &out, &olen);
+ if (rc != SASL_OK) {
+ gchar *error =
+ g_strdup_printf(_("SASL error: %s"),
+ sasl_errdetail(js->sasl));
+ purple_debug_error("jabber",
+ "sasl_encode error %d: %s\n", rc,
+ sasl_errdetail(js->sasl));
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ error);
+ g_free(error);
+ return;
+ }
pos += towrite;
+ /* do_jabber_send_raw returns FALSE when it throws a
+ * connection error.
+ */
if (!do_jabber_send_raw(js, out, olen))
break;
}
@@ -475,9 +543,6 @@ void jabber_send_raw(JabberStream *js, const char *data, int len)
}
#endif
- if (len == -1)
- len = strlen(data);
-
if (js->bosh)
jabber_bosh_connection_send_raw(js->bosh, data);
else
@@ -486,7 +551,14 @@ void jabber_send_raw(JabberStream *js, const char *data, int len)
int jabber_prpl_send_raw(PurpleConnection *gc, const char *buf, int len)
{
- JabberStream *js = (JabberStream*)gc->proto_data;
+ JabberStream *js = purple_connection_get_protocol_data(gc);
+
+ g_return_val_if_fail(js != NULL, -1);
+ /* TODO: It's probably worthwhile to restrict this to when the account
+ * state is CONNECTED, but I can /almost/ envision reasons for wanting
+ * to do things during the connection process.
+ */
+
jabber_send_raw(js, buf, len);
return len;
}
@@ -501,8 +573,7 @@ void jabber_send_signal_cb(PurpleConnection *pc, xmlnode **packet,
if (NULL == packet)
return;
- if (!PURPLE_CONNECTION_IS_VALID(pc))
- return;
+ g_return_if_fail(PURPLE_CONNECTION_IS_VALID(pc));
js = purple_connection_get_protocol_data(pc);
@@ -559,7 +630,7 @@ jabber_recv_cb_ssl(gpointer data, PurpleSslConnection *gsc,
/* TODO: It should be possible to make this check unnecessary */
if(!PURPLE_CONNECTION_IS_VALID(gc)) {
purple_ssl_close(gsc);
- return;
+ g_return_if_reached();
}
while((len = purple_ssl_read(gsc, buf, sizeof(buf) - 1)) > 0) {
@@ -590,24 +661,35 @@ static void
jabber_recv_cb(gpointer data, gint source, PurpleInputCondition condition)
{
PurpleConnection *gc = data;
- JabberStream *js = gc->proto_data;
+ JabberStream *js = purple_connection_get_protocol_data(gc);
int len;
static char buf[4096];
- if(!PURPLE_CONNECTION_IS_VALID(gc))
- return;
+ g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc));
if((len = read(js->fd, buf, sizeof(buf) - 1)) > 0) {
gc->last_received = time(NULL);
#ifdef HAVE_CYRUS_SASL
- if (js->sasl_maxbuf>0) {
+ if (js->sasl_maxbuf > 0) {
const char *out;
unsigned int olen;
- sasl_decode(js->sasl, buf, len, &out, &olen);
- if (olen>0) {
+ int rc;
+
+ rc = sasl_decode(js->sasl, buf, len, &out, &olen);
+ if (rc != SASL_OK) {
+ gchar *error =
+ g_strdup_printf(_("SASL error: %s"),
+ sasl_errdetail(js->sasl));
+ purple_debug_error("jabber",
+ "sasl_decode_error %d: %s\n", rc,
+ sasl_errdetail(js->sasl));
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ error);
+ } else if (olen > 0) {
purple_debug_info("jabber", "RecvSASL (%u): %s\n", olen, out);
- jabber_parser_process(js,out,olen);
- if(js->reinit)
+ jabber_parser_process(js, out, olen);
+ if (js->reinit)
jabber_stream_init(js);
}
return;
@@ -643,7 +725,7 @@ jabber_login_callback_ssl(gpointer data, PurpleSslConnection *gsc,
/* TODO: It should be possible to make this check unnecessary */
if(!PURPLE_CONNECTION_IS_VALID(gc)) {
purple_ssl_close(gsc);
- return;
+ g_return_if_reached();
}
js = gc->proto_data;
@@ -738,8 +820,7 @@ jabber_ssl_connect_failure(PurpleSslConnection *gsc, PurpleSslErrorType error,
JabberStream *js;
/* If the connection is already disconnected, we don't need to do anything else */
- if(!PURPLE_CONNECTION_IS_VALID(gc))
- return;
+ g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc));
js = gc->proto_data;
js->gsc = NULL;
@@ -842,6 +923,16 @@ jabber_stream_new(PurpleAccount *account)
purple_connection_error_reason(gc,
PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
_("Invalid XMPP ID"));
+ g_free(user);
+ /* Destroying the connection will free the JabberStream */
+ return NULL;
+ }
+
+ if (!js->user->node || *(js->user->node) == '\0') {
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
+ _("Invalid XMPP ID. Username portion must be set."));
+ g_free(user);
/* Destroying the connection will free the JabberStream */
return NULL;
}
@@ -850,6 +941,7 @@ jabber_stream_new(PurpleAccount *account)
purple_connection_error_reason(gc,
PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
_("Invalid XMPP ID. Domain must be set."));
+ g_free(user);
/* Destroying the connection will free the JabberStream */
return NULL;
}
@@ -878,6 +970,7 @@ jabber_stream_new(PurpleAccount *account)
js->write_buffer = purple_circ_buffer_new(512);
js->old_length = 0;
js->keepalive_timeout = 0;
+ js->max_inactivity = DEFAULT_INACTIVITY_TIME;
/* Set the default protocol version to 1.0. Overridden in parser.c. */
js->protocol_version.major = 1;
js->protocol_version.minor = 0;
@@ -927,7 +1020,7 @@ jabber_stream_connect(JabberStream *js)
js->certificate_CN = g_strdup(connect_server[0] ? connect_server : js->user->domain);
/* if they've got old-ssl mode going, we probably want to ignore SRV lookups */
- if(purple_account_get_bool(account, "old_ssl", FALSE)) {
+ if (g_str_equal("old_ssl", purple_account_get_string(account, "connection_security", JABBER_DEFAULT_REQUIRE_TLS))) {
if(purple_ssl_is_supported()) {
js->gsc = purple_ssl_connect(account, js->certificate_CN,
purple_account_get_int(account, "port", 5223),
@@ -1532,7 +1625,8 @@ void jabber_close(PurpleConnection *gc)
g_free(js->avatar_hash);
g_free(js->caps_hash);
- purple_circ_buffer_destroy(js->write_buffer);
+ if (js->write_buffer)
+ purple_circ_buffer_destroy(js->write_buffer);
if(js->writeh)
purple_input_remove(js->writeh);
if (js->auth_mech && js->auth_mech->dispose)
@@ -1543,6 +1637,8 @@ void jabber_close(PurpleConnection *gc)
if(js->sasl_mechs)
g_string_free(js->sasl_mechs, TRUE);
g_free(js->sasl_cb);
+ /* Note: _not_ g_free. See auth_cyrus.c:jabber_sasl_cb_secret */
+ free(js->sasl_secret);
#endif
g_free(js->serverFQDN);
while(js->commands) {
@@ -1564,13 +1660,14 @@ void jabber_close(PurpleConnection *gc)
g_free(js->old_source);
g_free(js->old_uri);
g_free(js->old_track);
- g_free(js->expected_rspauth);
if (js->vcard_timer != 0)
purple_timeout_remove(js->vcard_timer);
if (js->keepalive_timeout != 0)
purple_timeout_remove(js->keepalive_timeout);
+ if (js->inactivity_timer != 0)
+ purple_timeout_remove(js->inactivity_timer);
g_free(js->srv_rec);
js->srv_rec = NULL;
@@ -1622,6 +1719,9 @@ void jabber_stream_set_state(JabberStream *js, JabberStreamState state)
case JABBER_STREAM_CONNECTED:
/* Send initial presence */
jabber_presence_send(js, TRUE);
+ /* Start up the inactivity timer */
+ jabber_stream_restart_inactivity_timer(js);
+
purple_connection_set_state(js->gc, PURPLE_CONNECTED);
break;
}
@@ -1867,20 +1967,53 @@ static void jabber_features_destroy(void)
}
}
-void jabber_add_identity(const gchar *category, const gchar *type, const gchar *lang, const gchar *name) {
+gint
+jabber_identity_compare(gconstpointer a, gconstpointer b)
+{
+ const JabberIdentity *ac;
+ const JabberIdentity *bc;
+ gint cat_cmp;
+ gint typ_cmp;
+
+ ac = a;
+ bc = b;
+
+ if ((cat_cmp = strcmp(ac->category, bc->category)) == 0) {
+ if ((typ_cmp = strcmp(ac->type, bc->type)) == 0) {
+ if (!ac->lang && !bc->lang) {
+ return 0;
+ } else if (ac->lang && !bc->lang) {
+ return 1;
+ } else if (!ac->lang && bc->lang) {
+ return -1;
+ } else {
+ return strcmp(ac->lang, bc->lang);
+ }
+ } else {
+ return typ_cmp;
+ }
+ } else {
+ return cat_cmp;
+ }
+}
+
+void jabber_add_identity(const gchar *category, const gchar *type,
+ const gchar *lang, const gchar *name)
+{
GList *identity;
JabberIdentity *ident;
+
/* both required according to XEP-0030 */
g_return_if_fail(category != NULL);
g_return_if_fail(type != NULL);
- for(identity = jabber_identities; identity; identity = identity->next) {
- JabberIdentity *ident = (JabberIdentity*)identity->data;
- if (!strcmp(ident->category, category) &&
- !strcmp(ident->type, type) &&
- ((!ident->lang && !lang) || (ident->lang && lang && !strcmp(ident->lang, lang)))) {
+ /* Check if this identity is already there... */
+ for (identity = jabber_identities; identity; identity = identity->next) {
+ JabberIdentity *id = identity->data;
+ if (g_str_equal(id->category, category) &&
+ g_str_equal(id->type, type) &&
+ purple_strequal(id->lang, lang))
return;
- }
}
ident = g_new0(JabberIdentity, 1);
@@ -1888,7 +2021,8 @@ void jabber_add_identity(const gchar *category, const gchar *type, const gchar *
ident->type = g_strdup(type);
ident->lang = g_strdup(lang);
ident->name = g_strdup(name);
- jabber_identities = g_list_prepend(jabber_identities, ident);
+ jabber_identities = g_list_insert_sorted(jabber_identities, ident,
+ jabber_identity_compare);
}
static void jabber_identities_destroy(void)
@@ -1910,6 +2044,38 @@ gboolean jabber_stream_is_ssl(JabberStream *js)
(!js->bosh && js->gsc);
}
+static gboolean
+inactivity_cb(gpointer data)
+{
+ JabberStream *js = data;
+
+ /* We want whatever is sent to set this. It's okay because
+ * the eventloop unsets it via the return FALSE.
+ */
+ js->inactivity_timer = 0;
+
+ if (js->bosh)
+ jabber_bosh_connection_send_keepalive(js->bosh);
+ else
+ jabber_send_raw(js, "\t", 1);
+
+ return FALSE;
+}
+
+void jabber_stream_restart_inactivity_timer(JabberStream *js)
+{
+ if (js->inactivity_timer != 0) {
+ purple_timeout_remove(js->inactivity_timer);
+ js->inactivity_timer = 0;
+ }
+
+ g_return_if_fail(js->max_inactivity > 0);
+
+ js->inactivity_timer =
+ purple_timeout_add_seconds(js->max_inactivity,
+ inactivity_cb, js);
+}
+
const char *jabber_list_icon(PurpleAccount *a, PurpleBuddy *b)
{
return "jabber";
@@ -3313,8 +3479,7 @@ gboolean jabber_can_receive_file(PurpleConnection *gc, const char *who)
for (iter = jb->resources; iter ; iter = g_list_next(iter)) {
JabberBuddyResource *jbr = (JabberBuddyResource *) iter->data;
- if (jabber_resource_has_capability(jbr,
- "http://jabber.org/protocol/si/profile/file-transfer")
+ if (jabber_resource_has_capability(jbr, NS_SI_FILE_TRANSFER)
&& (jabber_resource_has_capability(jbr,
NS_BYTESTREAMS)
|| jabber_resource_has_capability(jbr, NS_IBB))) {
@@ -3336,7 +3501,7 @@ jabber_cmd_mood(PurpleConversation *conv,
if (js->pep) {
/* if no argument was given, unset mood */
- if (!args | !args[0]) {
+ if (!args || !args[0]) {
jabber_mood_set(js, NULL, NULL);
} else if (!args[1]) {
jabber_mood_set(js, args[0], NULL);
@@ -3616,7 +3781,7 @@ jabber_do_init(void)
jabber_add_feature("http://jabber.org/protocol/muc", 0);
jabber_add_feature("http://jabber.org/protocol/muc#user", 0);
jabber_add_feature("http://jabber.org/protocol/si", 0);
- jabber_add_feature("http://jabber.org/protocol/si/profile/file-transfer", 0);
+ jabber_add_feature(NS_SI_FILE_TRANSFER, 0);
jabber_add_feature(NS_XHTML_IM, 0);
jabber_add_feature(NS_PING, 0);
@@ -3675,6 +3840,11 @@ jabber_do_uninit(void)
jabber_presence_uninit();
jabber_iq_uninit();
+#ifdef USE_VV
+ g_signal_handlers_disconnect_by_func(G_OBJECT(purple_media_manager_get()),
+ G_CALLBACK(jabber_caps_broadcast_change), NULL);
+#endif
+
jabber_auth_uninit();
jabber_features_destroy();
jabber_identities_destroy();
diff --git a/libpurple/protocols/jabber/jabber.h b/libpurple/protocols/jabber/jabber.h
index 710fd33aa2..dda194af91 100644
--- a/libpurple/protocols/jabber/jabber.h
+++ b/libpurple/protocols/jabber/jabber.h
@@ -80,7 +80,7 @@ typedef struct _JabberStream JabberStream;
#define CAPS0115_NODE "http://pidgin.im/"
-#define JABBER_DEFAULT_REQUIRE_TLS TRUE
+#define JABBER_DEFAULT_REQUIRE_TLS "require_starttls"
#define JABBER_DEFAULT_FT_PROXIES "proxy.eu.jabber.org"
/* Index into attention_types list */
@@ -112,7 +112,7 @@ struct _JabberStream
JabberSaslMech *auth_mech;
gpointer auth_mech_data;
-
+
/**
* The header from the opening <stream/> tag. This being NULL is treated
* as a special condition in the parsing code (signifying the next
@@ -122,9 +122,6 @@ struct _JabberStream
char *stream_id;
JabberStreamState state;
- /* SASL authentication */
- char *expected_rspauth;
-
GHashTable *buddies;
/*
@@ -209,6 +206,7 @@ struct _JabberStream
#ifdef HAVE_CYRUS_SASL
sasl_conn_t *sasl;
sasl_callback_t *sasl_cb;
+ sasl_secret_t *sasl_secret;
const char *current_mech;
int auth_fail_count;
@@ -255,6 +253,8 @@ struct _JabberStream
/* A purple timeout tag for the keepalive */
guint keepalive_timeout;
+ guint max_inactivity;
+ guint inactivity_timer;
PurpleSrvResponse *srv_rec;
guint srv_rec_idx;
@@ -304,6 +304,9 @@ typedef struct _JabberBytestreamsStreamhost {
/* what kind of additional features as returned from disco#info are supported? */
extern GList *jabber_features;
+/* A sorted list of identities advertised. Use jabber_add_identity to add
+ * so it remains sorted.
+ */
extern GList *jabber_identities;
void jabber_stream_features_parse(JabberStream *js, xmlnode *packet);
@@ -343,12 +346,24 @@ void jabber_remove_feature(const gchar *namespace);
void jabber_add_identity(const gchar *category, const gchar *type, const gchar *lang, const gchar *name);
/**
+ * GCompareFunc for JabberIdentity structs.
+ */
+gint jabber_identity_compare(gconstpointer a, gconstpointer b);
+
+/**
* Returns true if this connection is over a secure (SSL) stream. Use this
* instead of checking js->gsc because BOSH stores its PurpleSslConnection
* members in its own data structure.
*/
gboolean jabber_stream_is_ssl(JabberStream *js);
+/**
+ * Restart the "we haven't sent anything in a while and should send
+ * something or the server will kick us off" timer (obviously
+ * called when sending something. It's exposed for BOSH.)
+ */
+void jabber_stream_restart_inactivity_timer(JabberStream *js);
+
/** PRPL functions */
const char *jabber_list_icon(PurpleAccount *a, PurpleBuddy *b);
const char* jabber_list_emblem(PurpleBuddy *b);
diff --git a/libpurple/protocols/jabber/jingle/transport.c b/libpurple/protocols/jabber/jingle/transport.c
index 1a465bbb2a..b5d1c342e1 100644
--- a/libpurple/protocols/jabber/jingle/transport.c
+++ b/libpurple/protocols/jabber/jingle/transport.c
@@ -111,8 +111,6 @@ jingle_transport_set_property (GObject *object, guint prop_id, const GValue *val
JingleTransport *transport;
g_return_if_fail(JINGLE_IS_TRANSPORT(object));
- transport = JINGLE_TRANSPORT(object);
-
switch (prop_id) {
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -125,8 +123,6 @@ jingle_transport_get_property (GObject *object, guint prop_id, GValue *value, GP
{
JingleTransport *transport;
g_return_if_fail(JINGLE_IS_TRANSPORT(object));
-
- transport = JINGLE_TRANSPORT(object);
switch (prop_id) {
default:
diff --git a/libpurple/protocols/jabber/jutil.c b/libpurple/protocols/jabber/jutil.c
index 0de8a1854e..c128c71964 100644
--- a/libpurple/protocols/jabber/jutil.c
+++ b/libpurple/protocols/jabber/jutil.c
@@ -303,8 +303,10 @@ char *jabber_saslprep(const char *in)
c = (const guchar *)in;
for ( ; *c; ++c) {
- if (*c > 0x7f ||
+ if (*c > 0x7f || /* Non-ASCII characters */
+ *c == 0x7f || /* ASCII Delete character */
(*c < 0x20 && *c != '\t' && *c != '\n' && *c != '\r'))
+ /* ASCII control characters */
return NULL;
}
diff --git a/libpurple/protocols/jabber/libxmpp.c b/libpurple/protocols/jabber/libxmpp.c
index 87a59b5f53..17bbd019e4 100644
--- a/libpurple/protocols/jabber/libxmpp.c
+++ b/libpurple/protocols/jabber/libxmpp.c
@@ -127,7 +127,9 @@ static PurplePluginProtocolInfo prpl_info =
NULL, /* get_account_text_table */
jabber_initiate_media, /* initiate_media */
jabber_get_media_caps, /* get_media_caps */
- jabber_get_moods /* get_moods */
+ jabber_get_moods, /* get_moods */
+ NULL, /* set_public_alias */
+ NULL /* get_public_alias */
};
static gboolean load_plugin(PurplePlugin *plugin)
@@ -251,6 +253,7 @@ 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"), NULL, '@');
@@ -261,13 +264,26 @@ init_plugin(PurplePlugin *plugin)
purple_account_user_split_set_reverse(split, FALSE);
prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
- option = purple_account_option_bool_new(_("Require SSL/TLS"), "require_tls", JABBER_DEFAULT_REQUIRE_TLS);
- prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
- option);
+#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_bool_new(_("Force old (port 5223) SSL"), "old_ssl", FALSE);
+ 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);
option = purple_account_option_bool_new(
_("Allow plaintext auth over unencrypted streams"),
diff --git a/libpurple/protocols/jabber/message.c b/libpurple/protocols/jabber/message.c
index 82049a4064..db68b6e784 100644
--- a/libpurple/protocols/jabber/message.c
+++ b/libpurple/protocols/jabber/message.c
@@ -75,12 +75,8 @@ static void handle_chat(JabberMessage *jm)
jbr = jabber_buddy_find_resource(jb, jid->resource);
if(!jm->xhtml && !jm->body) {
- if (jbr) {
- if (jm->chat_state != JM_STATE_NONE)
- jbr->chat_states = JABBER_CHAT_STATES_SUPPORTED;
- else
- jbr->chat_states = JABBER_CHAT_STATES_UNSUPPORTED;
- }
+ if (jbr && jm->chat_state != JM_STATE_NONE)
+ jbr->chat_states = JABBER_CHAT_STATES_SUPPORTED;
if(JM_STATE_COMPOSING == jm->chat_state) {
serv_got_typing(gc, jm->from, 0, PURPLE_TYPING);
@@ -142,10 +138,13 @@ static void handle_chat(JabberMessage *jm)
}
if(jbr) {
- if (jm->chat_state != JM_STATE_NONE)
- jbr->chat_states = JABBER_CHAT_STATES_SUPPORTED;
- else
- jbr->chat_states = JABBER_CHAT_STATES_UNSUPPORTED;
+ /* Treat SUPPORTED as a terminal with no escape :) */
+ if (jbr->chat_states != JABBER_CHAT_STATES_SUPPORTED) {
+ if (jm->chat_state != JM_STATE_NONE)
+ jbr->chat_states = JABBER_CHAT_STATES_SUPPORTED;
+ else
+ jbr->chat_states = JABBER_CHAT_STATES_UNSUPPORTED;
+ }
if(jbr->thread_id)
g_free(jbr->thread_id);
@@ -297,7 +296,6 @@ static void handle_error(JabberMessage *jm)
}
static void handle_buzz(JabberMessage *jm) {
- PurpleBuddy *buddy;
PurpleAccount *account;
/* Delayed buzz MUST NOT be accepted */
@@ -310,7 +308,7 @@ static void handle_buzz(JabberMessage *jm) {
account = purple_connection_get_account(jm->js->gc);
- if ((buddy = purple_find_buddy(account, jm->from)) == NULL)
+ if (purple_find_buddy(account, jm->from) == NULL)
return; /* Do not accept buzzes from unknown people */
/* xmpp only has 1 attention type, so index is 0 */
@@ -588,8 +586,10 @@ void jabber_message_parse(JabberStream *js, xmlnode *packet)
jm->thread_id = xmlnode_get_data(child);
} else if(!strcmp(child->name, "body") && !strcmp(xmlns, NS_XMPP_CLIENT)) {
if(!jm->body) {
- char *msg = xmlnode_to_str(child, NULL);
- jm->body = purple_strdup_withhtml(msg);
+ char *msg = 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)) {
@@ -963,7 +963,7 @@ jabber_message_smileyfy_xhtml(JabberMessage *jm, const char *xhtml)
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), js);
+ 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));
@@ -1240,22 +1240,34 @@ int jabber_message_send_chat(PurpleConnection *gc, int id, const char *msg, Purp
unsigned int jabber_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state)
{
+ JabberStream *js;
JabberMessage *jm;
JabberBuddy *jb;
JabberBuddyResource *jbr;
- char *resource = jabber_get_resource(who);
+ char *resource;
- jb = jabber_buddy_find(gc->proto_data, who, TRUE);
- jbr = jabber_buddy_find_resource(jb, resource);
+ js = purple_connection_get_protocol_data(gc);
+ jb = jabber_buddy_find(js, who, TRUE);
+ if (!jb)
+ return 0;
+ resource = jabber_get_resource(who);
+ jbr = jabber_buddy_find_resource(jb, resource);
g_free(resource);
- if (!jbr || (jbr->chat_states == JABBER_CHAT_STATES_UNSUPPORTED))
+ /* We know this entity doesn't support chat states */
+ if (jbr && jbr->chat_states == JABBER_CHAT_STATES_UNSUPPORTED)
+ return 0;
+
+ /* *If* we don't have presence /and/ the buddy can't see our
+ * presence, don't send typing notifications.
+ */
+ if (!jbr && !(jb->subscription & JABBER_SUB_FROM))
return 0;
/* TODO: figure out threading */
jm = g_new0(JabberMessage, 1);
- jm->js = gc->proto_data;
+ jm->js = js;
jm->type = JABBER_MESSAGE_CHAT;
jm->to = g_strdup(who);
jm->id = jabber_get_next_id(jm->js);
diff --git a/libpurple/protocols/jabber/namespaces.h b/libpurple/protocols/jabber/namespaces.h
index 243ef8e52d..12c42411c1 100644
--- a/libpurple/protocols/jabber/namespaces.h
+++ b/libpurple/protocols/jabber/namespaces.h
@@ -61,6 +61,9 @@
#define NS_AVATAR_1_1_DATA "urn:xmpp:avatar:data"
#define NS_AVATAR_1_1_METADATA "urn:xmpp:avatar:metadata"
+/* XEP-0096 SI File Transfer */
+#define NS_SI_FILE_TRANSFER "http://jabber.org/protocol/si/profile/file-transfer"
+
/* XEP-0124 Bidirectional-streams Over Synchronous HTTP (BOSH) */
#define NS_BOSH "http://jabber.org/protocol/httpbind"
@@ -89,6 +92,9 @@
/* XEP-0237 Roster Versioning */
#define NS_ROSTER_VERSIONING "urn:xmpp:features:rosterver"
+/* XEP-0264 File Transfer Thumbnails (Thumbs) */
+#define NS_THUMBS "urn:xmpp:thumbs:0"
+
/* Google extensions */
#define NS_GOOGLE_CAMERA "http://www.google.com/xmpp/protocol/camera/v1"
#define NS_GOOGLE_VIDEO "http://www.google.com/xmpp/protocol/video/v1"
diff --git a/libpurple/protocols/jabber/oob.c b/libpurple/protocols/jabber/oob.c
index 63c2213291..430d17ac7e 100644
--- a/libpurple/protocols/jabber/oob.c
+++ b/libpurple/protocols/jabber/oob.c
@@ -185,7 +185,7 @@ static void jabber_oob_xfer_recv_denied(PurpleXfer *xfer) {
jabber_oob_xfer_recv_error(xfer, "406");
}
-static void jabber_oob_xfer_recv_canceled(PurpleXfer *xfer) {
+static void jabber_oob_xfer_recv_cancelled(PurpleXfer *xfer) {
jabber_oob_xfer_recv_error(xfer, "404");
}
@@ -233,7 +233,7 @@ void jabber_oob_parse(JabberStream *js, const char *from, JabberIqType type,
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_canceled);
+ 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);
diff --git a/libpurple/protocols/jabber/parser.c b/libpurple/protocols/jabber/parser.c
index 2c0dae610f..7e6e622c58 100644
--- a/libpurple/protocols/jabber/parser.c
+++ b/libpurple/protocols/jabber/parser.c
@@ -93,10 +93,25 @@ jabber_parser_element_start_libxml(void *user_data,
}
}
- if (js->stream_id == NULL)
+ if (js->stream_id == NULL) {
+#if 0
+ /* 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_AUTHENTICATION_IMPOSSIBLE,
_("XMPP stream missing ID"));
+#else
+ /* Instead, let's make up a placeholder stream ID, which we need
+ * to do because we flag on it being NULL as a special case
+ * in this parsing code.
+ */
+ js->stream_id = g_strdup("");
+ purple_debug_info("jabber", "Server failed to specify a stream "
+ "ID (underspecified in rfc3920, but intended "
+ "to be a MUST; digest legacy auth may fail.");
+#endif
+ }
} else {
if(js->current)
diff --git a/libpurple/protocols/jabber/presence.c b/libpurple/protocols/jabber/presence.c
index f89db8af1b..895c417fc8 100644
--- a/libpurple/protocols/jabber/presence.c
+++ b/libpurple/protocols/jabber/presence.c
@@ -824,6 +824,7 @@ handle_presence_contact(JabberStream *js, JabberPresence *presence)
if (presence->jb != js->user_jb) {
purple_debug_warning("jabber", "Got presence for unknown buddy %s on account %s (%p)\n",
buddy_name, purple_account_get_username(account), account);
+ g_free(buddy_name);
return FALSE;
} else {
/* this is a different resource of our own account. Resume even when this account isn't on our blist */
@@ -924,8 +925,9 @@ void jabber_presence_parse(JabberStream *js, xmlnode *packet)
goto out;
}
- presence.chat = jabber_chat_find(js, presence.jid_from->node,
- presence.jid_from->domain);
+ if (presence.jid_from->node)
+ presence.chat = jabber_chat_find(js, presence.jid_from->node,
+ presence.jid_from->domain);
if(presence.jb->error_msg) {
g_free(presence.jb->error_msg);
presence.jb->error_msg = NULL;
diff --git a/libpurple/protocols/jabber/roster.c b/libpurple/protocols/jabber/roster.c
index febf70bf47..b5acb7cf35 100644
--- a/libpurple/protocols/jabber/roster.c
+++ b/libpurple/protocols/jabber/roster.c
@@ -77,12 +77,9 @@ static void roster_request_cb(JabberStream *js, const char *from,
void jabber_roster_request(JabberStream *js)
{
- PurpleAccount *account;
JabberIq *iq;
xmlnode *query;
- account = purple_connection_get_account(js->gc);
-
iq = jabber_iq_new_query(js, JABBER_IQ_GET, "jabber:iq:roster");
query = xmlnode_get_child(iq->node, "query");
diff --git a/libpurple/protocols/jabber/si.c b/libpurple/protocols/jabber/si.c
index 759ac0ba66..d42640bcc3 100644
--- a/libpurple/protocols/jabber/si.c
+++ b/libpurple/protocols/jabber/si.c
@@ -32,6 +32,7 @@
#include "notify.h"
#include "buddy.h"
+#include "data.h"
#include "disco.h"
#include "jabber.h"
#include "ibb.h"
@@ -39,6 +40,7 @@
#include "si.h"
#define STREAMHOST_CONNECT_TIMEOUT 15
+#define ENABLE_FT_THUMBNAILS 0
typedef struct _JabberSIXfer {
JabberStream *js;
@@ -1246,26 +1248,46 @@ static void jabber_si_xfer_send_request(PurpleXfer *xfer)
JabberIq *iq;
xmlnode *si, *file, *feature, *x, *field, *option, *value;
char buf[32];
+#if ENABLE_FT_THUMBNAILS
+ gconstpointer thumb;
+ gsize thumb_size;
+ purple_xfer_prepare_thumbnail(xfer, "jpeg,png");
+#endif
xfer->filename = g_path_get_basename(xfer->local_filename);
-
+
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");
jsx->stream_id = jabber_get_next_id(jsx->js);
xmlnode_set_attrib(si, "id", jsx->stream_id);
- xmlnode_set_attrib(si, "profile",
- "http://jabber.org/protocol/si/profile/file-transfer");
+ xmlnode_set_attrib(si, "profile", NS_SI_FILE_TRANSFER);
file = xmlnode_new_child(si, "file");
- xmlnode_set_namespace(file,
- "http://jabber.org/protocol/si/profile/file-transfer");
+ 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);
/* maybe later we'll do hash and date attribs */
+#if ENABLE_FT_THUMBNAILS
+ /* add thumbnail, if appropriate */
+ if ((thumb = purple_xfer_get_thumbnail(xfer, &thumb_size))) {
+ const gchar *mimetype = purple_xfer_get_thumbnail_mimetype(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",
+ jabber_data_get_cid(thumbnail_data));
+ 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");
@@ -1464,7 +1486,7 @@ static void do_transfer_send(PurpleXfer *xfer, const char *resource)
if (jabber_resource_has_capability(jbr, NS_IBB))
jsx->stream_method |= STREAM_METHOD_IBB;
- if (jabber_resource_has_capability(jbr, "http://jabber.org/protocol/si/profile/file-transfer")) {
+ if (jabber_resource_has_capability(jbr, NS_SI_FILE_TRANSFER)) {
jabber_si_xfer_send_request(xfer);
return;
}
@@ -1499,7 +1521,8 @@ static void jabber_si_xfer_init(PurpleXfer *xfer)
JabberBuddy *jb;
JabberBuddyResource *jbr = NULL;
char *resource;
-
+ GList *resources = NULL;
+
if(NULL != (resource = jabber_get_resource(xfer->who))) {
/* they've specified a resource, no need to ask or
* default or anything, just do it */
@@ -1511,7 +1534,22 @@ static void jabber_si_xfer_init(PurpleXfer *xfer)
jb = jabber_buddy_find(jsx->js, xfer->who, TRUE);
- if(!jb || !jb->resources) {
+ if (jb) {
+ GList *l;
+
+ for (l = jb->resources ; l ; l = g_list_next(l)) {
+ jbr = l->data;
+
+ if (!jabber_resource_know_capabilities(jbr) ||
+ (jabber_resource_has_capability(jbr, NS_SI_FILE_TRANSFER)
+ && (jabber_resource_has_capability(jbr, NS_BYTESTREAMS)
+ || jabber_resource_has_capability(jbr, NS_IBB)))) {
+ resources = g_list_append(resources, jbr);
+ }
+ }
+ }
+
+ if (!resources) {
/* no resources online, we're trying to send to someone
* whose presence we're not subscribed to, or
* someone who is offline. Let's inform the user */
@@ -1527,13 +1565,11 @@ static void jabber_si_xfer_init(PurpleXfer *xfer)
purple_notify_error(jsx->js->gc, _("File Send Failed"), _("File Send Failed"), msg);
g_free(msg);
- } else if(!jb->resources->next) {
+ } else if (g_list_length(resources) == 1) {
/* only 1 resource online (probably our most common case)
* so no need to ask who to send to */
- jbr = jb->resources->data;
-
+ jbr = resources->data;
do_transfer_send(xfer, jbr->name);
-
} else {
/* we've got multiple resources, we need to pick one to send to */
GList *l;
@@ -1541,11 +1577,9 @@ static void jabber_si_xfer_init(PurpleXfer *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);
-
- for(l = jb->resources; l; l = l->next)
- {
+
+ for(l = resources; l; l = l->next) {
jbr = l->data;
-
purple_request_field_choice_add(field, jbr->name);
}
@@ -1559,6 +1593,8 @@ static void jabber_si_xfer_init(PurpleXfer *xfer)
g_free(msg);
}
+
+ g_list_free(resources);
} else {
xmlnode *si, *feature, *x, *field, *value;
@@ -1630,12 +1666,8 @@ PurpleXfer *jabber_si_new_xfer(PurpleConnection *gc, const char *who)
void jabber_si_xfer_send(PurpleConnection *gc, const char *who, const char *file)
{
- JabberStream *js;
-
PurpleXfer *xfer;
- js = gc->proto_data;
-
xfer = jabber_si_new_xfer(gc, who);
if (file)
@@ -1644,17 +1676,34 @@ void jabber_si_xfer_send(PurpleConnection *gc, const char *who, const char *file
purple_xfer_request(xfer);
}
+#if ENABLE_FT_THUMBNAILS
+static void
+jabber_si_thumbnail_cb(JabberData *data, gchar *alt, gpointer userdata)
+{
+ PurpleXfer *xfer = (PurpleXfer *) userdata;
+
+ if (data) {
+ purple_xfer_set_thumbnail(xfer, jabber_data_get_data(data),
+ jabber_data_get_size(data), jabber_data_get_type(data));
+ /* data is ephemeral, get rid of now (the xfer re-owned the thumbnail */
+ jabber_data_destroy(data);
+ }
+
+ purple_xfer_request(xfer);
+}
+#endif
+
void jabber_si_parse(JabberStream *js, const char *from, JabberIqType type,
const char *id, xmlnode *si)
{
JabberSIXfer *jsx;
PurpleXfer *xfer;
- xmlnode *file, *feature, *x, *field, *option, *value;
+ xmlnode *file, *feature, *x, *field, *option, *value, *thumbnail;
const char *stream_id, *filename, *filesize_c, *profile;
size_t filesize = 0;
if(!(profile = xmlnode_get_attrib(si, "profile")) ||
- strcmp(profile, "http://jabber.org/protocol/si/profile/file-transfer"))
+ strcmp(profile, NS_SI_FILE_TRANSFER))
return;
if(!(stream_id = xmlnode_get_attrib(si, "id")))
@@ -1681,7 +1730,7 @@ void jabber_si_parse(JabberStream *js, const char *from, JabberIqType type,
/* if they've already sent us this file transfer with the same damn id
* then we're gonna ignore it, until I think of something better to do
* with it */
- if((xfer = jabber_si_xfer_find(js, stream_id, from)))
+ if(jabber_si_xfer_find(js, stream_id, from) != NULL)
return;
jsx = g_new0(JabberSIXfer, 1);
@@ -1731,10 +1780,27 @@ void jabber_si_parse(JabberStream *js, const char *from, JabberIqType type,
purple_xfer_set_request_denied_fnc(xfer, jabber_si_xfer_request_denied);
purple_xfer_set_cancel_recv_fnc(xfer, jabber_si_xfer_cancel_recv);
purple_xfer_set_end_fnc(xfer, jabber_si_xfer_end);
-
+
js->file_transfers = g_list_append(js->file_transfers, xfer);
+#if ENABLE_FT_THUMBNAILS
+ /* if there is a thumbnail, we should request it... */
+ if ((thumbnail = xmlnode_get_child_with_namespace(file, "thumbnail",
+ NS_THUMBS))) {
+ const char *cid = 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);
+ } else {
+ purple_xfer_request(xfer);
+ }
+ } else {
+ purple_xfer_request(xfer);
+ }
+#else
+ thumbnail = NULL; /* Silence warning */
purple_xfer_request(xfer);
+#endif
}
void
diff --git a/libpurple/protocols/jabber/xdata.c b/libpurple/protocols/jabber/xdata.c
index 64ca60586e..e681694396 100644
--- a/libpurple/protocols/jabber/xdata.c
+++ b/libpurple/protocols/jabber/xdata.c
@@ -411,4 +411,30 @@ void *jabber_x_data_request_with_actions(JabberStream *js, xmlnode *packet, GLis
return handle;
}
+gchar *
+jabber_x_data_get_formtype(const xmlnode *form)
+{
+ xmlnode *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");
+ if (purple_strequal(var, "FORM_TYPE")) {
+ xmlnode *value = xmlnode_get_child(field, "value");
+ if (value)
+ return xmlnode_get_data(value);
+ else
+ /* An interesting corner case... Looking for a second
+ * FORM_TYPE would be more considerate, but I'm in favor
+ * of not helping broken clients.
+ */
+ return NULL;
+ }
+ }
+
+ /* Erm, none found :( */
+ return NULL;
+}
diff --git a/libpurple/protocols/jabber/xdata.h b/libpurple/protocols/jabber/xdata.h
index c737fdeda3..7ebc50e008 100644
--- a/libpurple/protocols/jabber/xdata.h
+++ b/libpurple/protocols/jabber/xdata.h
@@ -37,4 +37,19 @@ typedef void (*jabber_x_data_action_cb)(JabberStream *js, xmlnode *result, const
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);
+/*
+ * Return the form type (the CDATA of the value child of the FORM_TYPE
+ * field entry.
+ * E.g., for the following, "http://jabber.org/protocol/muc#roominfo".
+ * <x xmlns='jabber:x:data' type='result'>
+ * <field var='FORM_TYPE' type='hidden'>
+ * <value>http://jabber.org/protocol/muc#roominfo</value>
+ * </field>
+ * </x>
+ *
+ * @param form The xmlnode for the form (the 'x' element)
+ * @returns The FORM_TYPE. Must be freed by caller.
+ */
+gchar *jabber_x_data_get_formtype(const xmlnode *form);
+
#endif /* PURPLE_JABBER_XDATA_H_ */
diff --git a/libpurple/protocols/msn/Makefile.am b/libpurple/protocols/msn/Makefile.am
index 9c887825cb..f911d5b2f3 100644
--- a/libpurple/protocols/msn/Makefile.am
+++ b/libpurple/protocols/msn/Makefile.am
@@ -14,6 +14,8 @@ MSNSOURCES = \
contact.h\
dialog.c \
dialog.h \
+ directconn.c \
+ directconn.h \
error.c \
error.h \
group.c \
diff --git a/libpurple/protocols/msn/Makefile.mingw b/libpurple/protocols/msn/Makefile.mingw
index 40d5ea9db8..fe75c1cb68 100644
--- a/libpurple/protocols/msn/Makefile.mingw
+++ b/libpurple/protocols/msn/Makefile.mingw
@@ -41,6 +41,7 @@ C_SRC = cmdproc.c \
command.c \
contact.c\
dialog.c \
+ directconn.c \
error.c \
group.c \
history.c \
diff --git a/libpurple/protocols/msn/contact.c b/libpurple/protocols/msn/contact.c
index c1a511c551..fd01527c3b 100644
--- a/libpurple/protocols/msn/contact.c
+++ b/libpurple/protocols/msn/contact.c
@@ -527,16 +527,20 @@ msn_get_contact_list_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
g_return_if_fail(session != NULL);
if (resp != NULL) {
+#ifdef MSN_PARTIAL_LISTS
const char *abLastChange;
const char *dynamicItemLastChange;
+#endif
purple_debug_misc("msn", "Got the contact list!\n");
msn_parse_contact_list(session, resp->xml);
+#ifdef MSN_PARTIAL_LISTS
abLastChange = purple_account_get_string(session->account,
"ablastChange", NULL);
dynamicItemLastChange = purple_account_get_string(session->account,
- "dynamicItemLastChange", NULL);
+ "DynamicItemLastChanged", NULL);
+#endif
if (state->partner_scenario == MSN_PS_INITIAL) {
#ifdef MSN_PARTIAL_LISTS
@@ -684,20 +688,20 @@ msn_parse_addressbook_contacts(MsnSession *session, xmlnode *node)
xmlnode *annotation;
MsnUser *user;
- if (!(contactId = xmlnode_get_child(contactNode,"contactId"))
- || !(contactInfo = xmlnode_get_child(contactNode, "contactInfo"))
- || !(contactType = xmlnode_get_child(contactInfo, "contactType")))
- continue;
-
g_free(passport);
g_free(Name);
- g_free(alias);
g_free(uid);
g_free(type);
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")))
+ continue;
+
uid = xmlnode_get_data(contactId);
type = xmlnode_get_data(contactType);
@@ -836,6 +840,7 @@ msn_parse_addressbook_contacts(MsnSession *session, xmlnode *node)
g_free(uid);
g_free(type);
g_free(mobile_number);
+ g_free(alias);
}
static gboolean
@@ -885,7 +890,7 @@ msn_parse_addressbook(MsnSession *session, xmlnode *node)
msn_parse_addressbook_groups(session, groups);
}
- /*add a default No group to set up the no group Membership*/
+ /* Add an "Other Contacts" group for buddies who aren't in a group */
msn_group_new(session->userlist, MSN_INDIVIDUALS_GROUP_ID,
MSN_INDIVIDUALS_GROUP_NAME);
purple_debug_misc("msn", "AB group_id:%s name:%s\n",
@@ -895,7 +900,7 @@ msn_parse_addressbook(MsnSession *session, xmlnode *node)
purple_blist_add_group(g, NULL);
}
- /*add a default No group to set up the no group Membership*/
+ /* 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) {
@@ -1564,7 +1569,7 @@ msn_del_contact_from_list(MsnSession *session, MsnCallbackState *state,
if (list == MSN_LIST_PL) {
partner_scenario = MSN_PS_CONTACT_API;
- if (user && user->networkid != MSN_NETWORK_PASSPORT)
+ if (user->networkid != MSN_NETWORK_PASSPORT)
member = g_strdup_printf(MSN_MEMBER_MEMBERSHIPID_XML,
"EmailMember", "Email",
user->member_id_on_pending_list);
diff --git a/libpurple/protocols/msn/directconn.c b/libpurple/protocols/msn/directconn.c
index e846d75ea1..6fb7fb72fd 100644
--- a/libpurple/protocols/msn/directconn.c
+++ b/libpurple/protocols/msn/directconn.c
@@ -27,479 +27,988 @@
#include "slp.h"
#include "slpmsg.h"
-/**************************************************************************
- * Directconn Specific
- **************************************************************************/
+#pragma pack(push,1)
+typedef struct {
+ guint32 session_id;
+ guint32 seq_id;
+ guint64 offset;
+ guint64 total_size;
+ guint32 length;
+ guint32 flags;
+ guint32 ack_id;
+ guint32 ack_uid;
+ guint64 ack_size;
+/* guint8 body[1]; */
+} MsnDcContext;
+#pragma pack(pop)
+
+#define DC_PACKET_HEADER_SIZE sizeof(MsnDcContext)
+#define DC_MAX_BODY_SIZE 8*1024
+#define DC_MAX_PACKET_SIZE (DC_PACKET_HEADER_SIZE + DC_MAX_BODY_SIZE)
-void
-msn_directconn_send_handshake(MsnDirectConn *directconn)
+static void
+msn_dc_calculate_nonce_hash(MsnDirectConnNonceType type,
+ const guchar nonce[16], gchar nonce_hash[37])
{
- MsnSlpLink *slplink;
- MsnSlpMessage *slpmsg;
+ 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, sizeof(nonce));
+ purple_cipher_context_digest(context, sizeof(digest), digest, NULL);
+ purple_cipher_context_destroy(context);
+ } else if (type == DC_NONCE_PLAIN) {
+ memcpy(digest, nonce, 16);
+ }
- g_return_if_fail(directconn != NULL);
+ g_sprintf(nonce_hash,
+ "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
- slplink = directconn->slplink;
+ digest[3],
+ digest[2],
+ digest[1],
+ digest[0],
- slpmsg = msn_slpmsg_new(slplink);
- slpmsg->flags = 0x100;
+ digest[5],
+ digest[4],
- if (directconn->nonce != NULL)
- {
- guint32 t1;
- guint16 t2;
- guint16 t3;
- guint16 t4;
- guint64 t5;
+ digest[7],
+ digest[6],
- sscanf (directconn->nonce, "%08X-%04hX-%04hX-%04hX-%012" G_GINT64_MODIFIER "X", &t1, &t2, &t3, &t4, &t5);
+ digest[8],
+ digest[9],
- t1 = GUINT32_TO_LE(t1);
- t2 = GUINT16_TO_LE(t2);
- t3 = GUINT16_TO_LE(t3);
- t4 = GUINT16_TO_BE(t4);
- t5 = GUINT64_TO_BE(t5);
+ digest[10],
+ digest[11],
+ digest[12],
+ digest[13],
+ digest[14],
+ digest[15]
+ );
+}
- slpmsg->ack_id = t1;
- slpmsg->ack_sub_id = t2 | (t3 << 16);
- slpmsg->ack_size = t4 | t5;
- }
+static void
+msn_dc_generate_nonce(MsnDirectConn *dc)
+{
+ guint32 *nonce;
+ int i;
- g_free(directconn->nonce);
+ nonce = (guint32 *)&dc->nonce;
+ for (i = 0; i < 4; i++)
+ nonce[i] = rand();
- msn_slplink_send_slpmsg(slplink, slpmsg);
+ msn_dc_calculate_nonce_hash(dc->nonce_type, dc->nonce, dc->nonce_hash);
- directconn->acked =TRUE;
+ if (purple_debug_is_verbose())
+ purple_debug_info("msn", "DC %p generated nonce %s\n", dc, dc->nonce_hash);
}
-/**************************************************************************
- * Connection Functions
- **************************************************************************/
+static MsnDirectConnPacket *
+msn_dc_new_packet(guint32 length)
+{
+ MsnDirectConnPacket *p;
+
+ p = g_new0(MsnDirectConnPacket, 1);
+ p->length = length;
+ p->data = g_malloc(length);
-static int
-create_listener(int port)
+ return p;
+}
+
+static void
+msn_dc_destroy_packet(MsnDirectConnPacket *p)
{
- int fd;
- int flags;
- const int on = 1;
+ g_free(p->data);
+
+ if (p->msg)
+ msn_message_unref(p->msg);
-#if 0
- struct addrinfo hints;
- struct addrinfo *c, *res;
- char port_str[5];
+ g_free(p);
+}
+
+MsnDirectConn *
+msn_dc_new(MsnSlpCall *slpcall)
+{
+ MsnDirectConn *dc;
+
+ g_return_val_if_fail(slpcall != NULL, NULL);
+
+ dc = g_new0(MsnDirectConn, 1);
+
+ if (purple_debug_is_verbose())
+ purple_debug_info("msn", "msn_dc_new %p\n", dc);
+
+ dc->slplink = slpcall->slplink;
+ dc->slpcall = slpcall;
+
+ if (dc->slplink->dc != NULL)
+ purple_debug_warning("msn", "msn_dc_new: slplink already has an allocated DC!\n");
+
+ dc->slplink->dc = dc;
+
+ dc->msg_body = NULL;
+ dc->prev_ack = NULL;
+ dc->listen_data = NULL;
+ dc->connect_data = NULL;
+ dc->listenfd = -1;
+ dc->listenfd_handle = 0;
+ dc->connect_timeout_handle = 0;
+ dc->fd = -1;
+ dc->recv_handle = 0;
+ dc->send_handle = 0;
+ dc->state = DC_STATE_CLOSED;
+ dc->in_buffer = NULL;
+ dc->out_queue = g_queue_new();
+ dc->msg_pos = -1;
+ dc->send_connection_info_msg_cb = NULL;
+ dc->ext_ip = NULL;
+ dc->timeout_handle = 0;
+ dc->progress = FALSE;
+ /*dc->num_calls = 1;*/
+
+ /* TODO: Probably should set this based on buddy caps */
+ dc->nonce_type = DC_NONCE_PLAIN;
+ msn_dc_generate_nonce(dc);
+
+ return dc;
+}
+
+void
+msn_dc_destroy(MsnDirectConn *dc)
+{
+ MsnSlpLink *slplink;
- snprintf(port_str, sizeof(port_str), "%d", port);
+ if (purple_debug_is_verbose())
+ purple_debug_info("msn", "msn_dc_destroy %p\n", dc);
- memset(&hints, 0, sizeof(hints));
+ g_return_if_fail(dc != NULL);
- hints.ai_flags = AI_PASSIVE;
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
+ if (dc->slpcall != NULL)
+ dc->slpcall->wait_for_socket = FALSE;
- if (getaddrinfo(NULL, port_str, &hints, &res) != 0)
- {
- purple_debug_error("msn", "Could not get address info: %s.\n",
- port_str);
- return -1;
+ slplink = dc->slplink;
+ if (slplink) {
+ slplink->dc = NULL;
+ if (slplink->swboard == NULL)
+ msn_slplink_destroy(slplink);
}
- for (c = res; c != NULL; c = c->ai_next)
- {
- fd = socket(c->ai_family, c->ai_socktype, c->ai_protocol);
+ g_free(dc->msg_body);
- if (fd < 0)
- continue;
+ if (dc->prev_ack) {
+ msn_slpmsg_destroy(dc->prev_ack);
+ }
- setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ if (dc->listen_data != NULL) {
+ purple_network_listen_cancel(dc->listen_data);
+ }
- if (bind(fd, c->ai_addr, c->ai_addrlen) == 0)
- break;
+ if (dc->connect_data != NULL) {
+ purple_proxy_connect_cancel(dc->connect_data);
+ }
- close(fd);
+ if (dc->listenfd != -1) {
+ purple_network_remove_port_mapping(dc->listenfd);
+ close(dc->listenfd);
}
- if (c == NULL)
- {
- purple_debug_error("msn", "Could not find socket: %s.\n", port_str);
- return -1;
+ if (dc->listenfd_handle != 0) {
+ purple_input_remove(dc->listenfd_handle);
}
- freeaddrinfo(res);
-#else
- struct sockaddr_in sockin;
+ if (dc->connect_timeout_handle != 0) {
+ purple_timeout_remove(dc->connect_timeout_handle);
+ }
- fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (dc->fd != -1) {
+ close(dc->fd);
+ }
- if (fd < 0)
- return -1;
+ if (dc->send_handle != 0) {
+ purple_input_remove(dc->send_handle);
+ }
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) != 0)
- {
- close(fd);
- return -1;
+ if (dc->recv_handle != 0) {
+ purple_input_remove(dc->recv_handle);
}
- memset(&sockin, 0, sizeof(struct sockaddr_in));
- sockin.sin_family = AF_INET;
- sockin.sin_port = htons(port);
+ g_free(dc->in_buffer);
- if (bind(fd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0)
- {
- close(fd);
- return -1;
+ if (dc->out_queue != NULL) {
+ while (!g_queue_is_empty(dc->out_queue))
+ msn_dc_destroy_packet( g_queue_pop_head(dc->out_queue) );
+
+ g_queue_free(dc->out_queue);
}
-#endif
- if (listen (fd, 4) != 0)
- {
- close (fd);
- return -1;
+ g_free(dc->ext_ip);
+
+ if (dc->timeout_handle != 0) {
+ purple_timeout_remove(dc->timeout_handle);
}
- flags = fcntl(fd, F_GETFL);
- fcntl(fd, F_SETFL, flags | O_NONBLOCK);
-#ifndef _WIN32
- fcntl(fd, F_SETFD, FD_CLOEXEC);
-#endif
+ g_free(dc);
+}
+
+/*
+void
+msn_dc_ref(MsnDirectConn *dc)
+{
+ g_return_if_fail(dc != NULL);
- return fd;
+ dc->num_calls++;
}
-static gssize
-msn_directconn_write(MsnDirectConn *directconn,
- const char *data, size_t len)
+void
+msn_dc_unref(MsnDirectConn *dc)
{
- char *buffer, *tmp;
- size_t buf_size;
- gssize ret;
- guint32 sent_len;
+ g_return_if_fail(dc != NULL);
- g_return_val_if_fail(directconn != NULL, 0);
- buf_size = len + 4;
- buffer = tmp = g_malloc(buf_size);
+ if (dc->num_calls > 0) {
+ dc->num_calls--;
+ }
+}
+*/
- sent_len = GUINT32_TO_LE(len);
+void
+msn_dc_send_invite(MsnDirectConn *dc)
+{
+ MsnSlpCall *slpcall;
+ MsnSlpMessage *msg;
+ gchar *header;
+
+ if (purple_debug_is_verbose())
+ purple_debug_info("msn", "msn_dc_send_invite %p\n", dc);
+
+ g_return_if_fail(dc != NULL);
+
+ slpcall = dc->slpcall;
+ g_return_if_fail(slpcall != NULL);
+
+ header = g_strdup_printf(
+ "INVITE MSNMSGR:%s MSNSLP/1.0",
+ slpcall->slplink->remote_user
+ );
+
+ msg = msn_slpmsg_sip_new(
+ slpcall,
+ 0,
+ header,
+ slpcall->branch,
+ "application/x-msnmsgr-transrespbody",
+ dc->msg_body
+ );
+ msg->info = "DC INVITE";
+ msg->text_body = TRUE;
+ g_free(header);
+ g_free(dc->msg_body);
+ dc->msg_body = NULL;
+
+ msn_slplink_queue_slpmsg(slpcall->slplink, msg);
+}
- memcpy(tmp, &sent_len, 4);
- tmp += 4;
- memcpy(tmp, data, len);
- tmp += len;
+void
+msn_dc_send_ok(MsnDirectConn *dc)
+{
+ if (purple_debug_is_verbose())
+ purple_debug_info("msn", "msn_dc_send_ok %p\n", dc);
- ret = write(directconn->fd, buffer, buf_size);
+ g_return_if_fail(dc != NULL);
-#ifdef DEBUG_DC
- char *str;
- str = g_strdup_printf("%s/msntest/w%.4d.bin", g_get_home_dir(), directconn->c);
+ msn_slp_send_ok(dc->slpcall, dc->slpcall->branch,
+ "application/x-msnmsgr-transrespbody", dc->msg_body);
+ g_free(dc->msg_body);
+ dc->msg_body = NULL;
- FILE *tf = g_fopen(str, "w");
- fwrite(buffer, 1, buf_size, tf);
- fclose(tf);
+ msn_slplink_send_slpmsg(dc->slpcall->slplink, dc->prev_ack);
+ msn_slpmsg_destroy(dc->prev_ack);
+ dc->prev_ack = NULL;
+ msn_slplink_send_queued_slpmsgs(dc->slpcall->slplink);
+}
- g_free(str);
-#endif
+void
+msn_dc_fallback_to_sb(MsnDirectConn *dc)
+{
+ MsnSlpLink *slplink;
+ MsnSlpCall *slpcall;
+ GQueue *queue = NULL;
+
+ purple_debug_info("msn", "msn_dc_fallback_to_sb %p\n", dc);
- g_free(buffer);
+ g_return_if_fail(dc != NULL);
- directconn->c++;
+ slpcall = dc->slpcall;
+ slplink = msn_slplink_ref(dc->slplink);
+ if (slpcall && !g_queue_is_empty(dc->out_queue)) {
+ queue = dc->out_queue;
+ dc->out_queue = NULL;
+ }
- return ret;
+ msn_dc_destroy(dc);
+
+ if (slpcall) {
+ msn_slpcall_session_init(slpcall);
+ if (queue) {
+ while (!g_queue_is_empty(queue)) {
+ MsnDirectConnPacket *p = g_queue_pop_head(queue);
+ msn_slplink_send_msg(slplink, p->msg);
+ msn_dc_destroy_packet(p);
+ }
+ g_queue_free(queue);
+ }
+ }
+ msn_slplink_unref(slplink);
}
-#if 0
-void
-msn_directconn_parse_nonce(MsnDirectConn *directconn, const char *nonce)
+static void
+msn_dc_parse_binary_header(MsnDirectConn *dc)
{
- guint32 t1;
- guint16 t2;
- guint16 t3;
- guint16 t4;
- guint64 t5;
+ MsnSlpHeader *h;
+ MsnDcContext *context;
+
+ g_return_if_fail(dc != NULL);
+
+ h = &dc->header;
+ /* Skip packet size */
+ context = (MsnDcContext *)(dc->in_buffer + 4);
+
+ h->session_id = GUINT32_FROM_LE(context->session_id);
+ h->id = GUINT32_FROM_LE(context->seq_id);
+ h->offset = GUINT64_FROM_LE(context->offset);
+ h->total_size = GUINT64_FROM_LE(context->total_size);
+ h->length = GUINT32_FROM_LE(context->length);
+ h->flags = GUINT32_FROM_LE(context->flags);
+ h->ack_id = GUINT32_FROM_LE(context->ack_id);
+ h->ack_sub_id = GUINT32_FROM_LE(context->ack_uid);
+ h->ack_size = GUINT64_FROM_LE(context->ack_size);
+}
- g_return_if_fail(directconn != NULL);
- g_return_if_fail(nonce != NULL);
+static const gchar *
+msn_dc_serialize_binary_header(MsnDirectConn *dc) {
+ MsnSlpHeader *h;
+ static MsnDcContext bin_header;
- sscanf (nonce, "%08X-%04hX-%04hX-%04hX-%012llX", &t1, &t2, &t3, &t4, &t5);
+ g_return_val_if_fail(dc != NULL, NULL);
- t1 = GUINT32_TO_LE(t1);
- t2 = GUINT16_TO_LE(t2);
- t3 = GUINT16_TO_LE(t3);
- t4 = GUINT16_TO_BE(t4);
- t5 = GUINT64_TO_BE(t5);
+ h = &dc->header;
- directconn->slpheader = g_new0(MsnSlpHeader, 1);
+ bin_header.session_id = GUINT32_TO_LE(h->session_id);
+ bin_header.seq_id = GUINT32_TO_LE(h->id);
+ bin_header.offset = GUINT64_TO_LE(h->offset);
+ bin_header.total_size = GUINT64_TO_LE(h->total_size);
+ bin_header.length = GUINT32_TO_LE(h->length);
+ bin_header.flags = GUINT32_TO_LE(h->flags);
+ bin_header.ack_id = GUINT32_TO_LE(h->ack_id);
+ bin_header.ack_uid = GUINT32_TO_LE(h->ack_sub_id);
+ bin_header.ack_size = GUINT64_TO_LE(h->ack_size);
- directconn->slpheader->ack_id = t1;
- directconn->slpheader->ack_sub_id = t2 | (t3 << 16);
- directconn->slpheader->ack_size = t4 | t5;
+ return (const gchar *)&bin_header;
}
-#endif
-void
-msn_directconn_send_msg(MsnDirectConn *directconn, MsnMessage *msg)
+static void
+msn_dc_send_cb(gpointer data, gint fd, PurpleInputCondition cond)
{
- char *body;
- size_t body_len;
+ MsnDirectConn *dc = data;
+ MsnDirectConnPacket *p;
+ int bytes_to_send;
+ int bytes_sent;
+
+ g_return_if_fail(dc != NULL);
+ g_return_if_fail(fd != -1);
+
+ if (g_queue_is_empty(dc->out_queue)) {
+ if (dc->send_handle != 0) {
+ purple_input_remove(dc->send_handle);
+ dc->send_handle = 0;
+ }
+ return;
+ }
+
+ p = g_queue_peek_head(dc->out_queue);
+
+ if (dc->msg_pos < 0) {
+ /* First we send the length of the packet */
+ guint32 len = GUINT32_TO_LE(p->length);
+ bytes_sent = send(fd, &len, 4, 0);
+ if (bytes_sent < 0) {
+ if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
+ return;
+
+ purple_debug_warning("msn", "msn_dc_send_cb: send error\n");
+ msn_dc_destroy(dc);
+ return;
+ }
+ dc->msg_pos = 0;
+ }
+
+ bytes_to_send = p->length - dc->msg_pos;
+ bytes_sent = send(fd, p->data + dc->msg_pos, bytes_to_send, 0);
+ if (bytes_sent < 0) {
+ if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
+ return;
+
+ purple_debug_warning("msn", "msn_dc_send_cb: send error\n");
+ msn_dc_destroy(dc);
+ return;
+ }
+
+ dc->progress = TRUE;
- body = msn_message_gen_slp_body(msg, &body_len);
+ dc->msg_pos += bytes_sent;
+ if (dc->msg_pos == p->length) {
+ if (p->sent_cb != NULL)
+ p->sent_cb(p);
- msn_directconn_write(directconn, body, body_len);
+ g_queue_pop_head(dc->out_queue);
+ msn_dc_destroy_packet(p);
+
+ dc->msg_pos = -1;
+ }
}
static void
-read_cb(gpointer data, gint source, PurpleInputCondition cond)
+msn_dc_enqueue_packet(MsnDirectConn *dc, MsnDirectConnPacket *p)
{
- MsnDirectConn* directconn;
- char *body;
- size_t body_len;
- gssize len;
-
- purple_debug_info("msn", "read_cb: %d, %d\n", source, cond);
-
- directconn = data;
-
- /* Let's read the length of the data. */
-#error This code is broken. See the note below.
- /*
- * TODO: This has problems! First of all, sizeof(body_len) will be
- * different on 32bit systems and on 64bit systems (4 bytes
- * vs. 8 bytes).
- * Secondly, we're reading from a TCP stream. There is no
- * guarantee that we have received the number of bytes we're
- * trying to read. We need to read into a buffer. If read
- * returns <0 then we need to check errno. If errno is EAGAIN
- * then don't destroy anything, just exit and wait for more
- * data. See every other function in libpurple that does this
- * correctly for an example.
- */
- len = read(directconn->fd, &body_len, sizeof(body_len));
-
- if (len <= 0)
- {
- /* ERROR */
- purple_debug_error("msn", "error reading\n");
-
- if (directconn->inpa)
- purple_input_remove(directconn->inpa);
-
- close(directconn->fd);
-
- msn_directconn_destroy(directconn);
+ gboolean was_empty;
- return;
+ was_empty = g_queue_is_empty(dc->out_queue);
+ g_queue_push_tail(dc->out_queue, p);
+
+ if (was_empty && dc->send_handle == 0) {
+ dc->send_handle = purple_input_add(dc->fd, PURPLE_INPUT_WRITE, msn_dc_send_cb, dc);
+ msn_dc_send_cb(dc, dc->fd, PURPLE_INPUT_WRITE);
}
+}
- body_len = GUINT32_FROM_LE(body_len);
+static void
+msn_dc_send_foo(MsnDirectConn *dc)
+{
+ MsnDirectConnPacket *p;
- purple_debug_info("msn", "body_len=%" G_GSIZE_FORMAT "\n", body_len);
+ if (purple_debug_is_verbose())
+ purple_debug_info("msn", "msn_dc_send_foo %p\n", dc);
- if (body_len <= 0)
- {
- /* ERROR */
- purple_debug_error("msn", "error reading\n");
+ p = msn_dc_new_packet(4);
- if (directconn->inpa)
- purple_input_remove(directconn->inpa);
+ memcpy(p->data, "foo\0", 4);
- close(directconn->fd);
+ msn_dc_enqueue_packet(dc, p);
+}
- msn_directconn_destroy(directconn);
+static void
+msn_dc_send_handshake_with_nonce(MsnDirectConn *dc, MsnDirectConnPacket *p)
+{
+ const gchar *h;
- return;
- }
+ h = msn_dc_serialize_binary_header(dc);
+ memcpy(p->data, h, DC_PACKET_HEADER_SIZE);
- body = g_try_malloc(body_len);
+ memcpy(p->data + offsetof(MsnDcContext, ack_id), dc->nonce, 16);
- if (body != NULL)
- {
- /* Let's read the data. */
- len = read(directconn->fd, body, body_len);
+ msn_dc_enqueue_packet(dc, p);
+}
- purple_debug_info("msn", "len=%" G_GSIZE_FORMAT "\n", len);
- }
- else
- {
- purple_debug_error("msn", "Failed to allocate memory for read\n");
- len = 0;
- }
+static void
+msn_dc_send_handshake(MsnDirectConn *dc)
+{
+ MsnDirectConnPacket *p;
- if (len > 0)
- {
- MsnMessage *msg;
+ p = msn_dc_new_packet(DC_PACKET_HEADER_SIZE);
-#ifdef DEBUG_DC
- str = g_strdup_printf("%s/msntest/r%.4d.bin", g_get_home_dir(), directconn->c);
+ dc->header.session_id = 0;
+ dc->header.id = dc->slpcall->slplink->slp_seq_id++;
+ dc->header.offset = 0;
+ dc->header.total_size = 0;
+ dc->header.length = 0;
+ dc->header.flags = 0x100;
- FILE *tf = g_fopen(str, "w");
- fwrite(body, 1, len, tf);
- fclose(tf);
+ msn_dc_send_handshake_with_nonce(dc, p);
+}
- g_free(str);
-#endif
+static void
+msn_dc_send_handshake_reply(MsnDirectConn *dc)
+{
+ MsnDirectConnPacket *p;
- directconn->c++;
+ p = msn_dc_new_packet(DC_PACKET_HEADER_SIZE);
- msg = msn_message_new_msnslp();
- msn_message_parse_slp_body(msg, body, body_len);
+ dc->header.id = dc->slpcall->slplink->slp_seq_id++;
+ dc->header.length = 0;
- purple_debug_info("msn", "directconn: process_msg\n");
- msn_slplink_process_msg(directconn->slplink, msg);
- }
- else
- {
- /* ERROR */
- purple_debug_error("msn", "error reading\n");
+ msn_dc_send_handshake_with_nonce(dc, p);
+}
- if (directconn->inpa)
- purple_input_remove(directconn->inpa);
+static gboolean
+msn_dc_verify_handshake(MsnDirectConn *dc, guint32 packet_length)
+{
+ guchar nonce[16];
+ gchar nonce_hash[37];
+
+ if (packet_length != DC_PACKET_HEADER_SIZE)
+ return FALSE;
+
+ memcpy(nonce, dc->in_buffer + 4 + offsetof(MsnDcContext, ack_id), 16);
+
+ if (dc->nonce_type == DC_NONCE_PLAIN) {
+ if (memcmp(dc->nonce, nonce, 16) == 0) {
+ purple_debug_info("msn",
+ "Nonce from buddy request and nonce from DC attempt match, "
+ "allowing direct connection\n");
+ return TRUE;
+ } else {
+ purple_debug_warning("msn",
+ "Nonce from buddy request and nonce from DC attempt "
+ "don't match, ignoring direct connection\n");
+ return FALSE;
+ }
+
+ } else if (dc->nonce_type == DC_NONCE_SHA1) {
+ msn_dc_calculate_nonce_hash(dc->nonce_type, nonce, nonce_hash);
+
+ if (g_str_equal(dc->remote_nonce, nonce_hash)) {
+ purple_debug_info("msn",
+ "Received nonce %s from buddy request "
+ "and calculated nonce %s from DC attempt. "
+ "Nonces match, allowing direct connection\n",
+ dc->remote_nonce, nonce_hash);
+ return TRUE;
+ } else {
+ purple_debug_warning("msn",
+ "Received nonce %s from buddy request "
+ "and calculated nonce %s from DC attempt. "
+ "Nonces don't match, ignoring direct connection\n",
+ dc->remote_nonce, nonce_hash);
+ return FALSE;
+ }
+ } else
+ return FALSE;
+}
+
+static void
+msn_dc_send_packet_cb(MsnDirectConnPacket *p)
+{
+ if (p->msg != NULL && p->msg->ack_cb != NULL)
+ p->msg->ack_cb(p->msg, p->msg->ack_data);
+}
+
+void
+msn_dc_enqueue_msg(MsnDirectConn *dc, MsnMessage *msg)
+{
+ MsnDirectConnPacket *p;
+ guint32 length;
+
+ length = msg->body_len + DC_PACKET_HEADER_SIZE;
+ p = msn_dc_new_packet(length);
+
+ memcpy(p->data, &msg->msnslp_header, DC_PACKET_HEADER_SIZE);
+ memcpy(p->data + DC_PACKET_HEADER_SIZE, msg->body, msg->body_len);
+
+ p->sent_cb = msn_dc_send_packet_cb;
+ p->msg = msn_message_ref(msg);
+
+ msn_dc_enqueue_packet(dc, p);
+}
+
+static int
+msn_dc_process_packet(MsnDirectConn *dc, guint32 packet_length)
+{
+ g_return_val_if_fail(dc != NULL, DC_PROCESS_ERROR);
+
+ switch (dc->state) {
+ case DC_STATE_CLOSED:
+ break;
+
+ case DC_STATE_FOO:
+ /* FOO message is always 4 bytes long */
+ if (packet_length != 4 || memcmp(dc->in_buffer, "\4\0\0\0foo", 8) != 0)
+ return DC_PROCESS_FALLBACK;
+
+ dc->state = DC_STATE_HANDSHAKE;
+ break;
- close(directconn->fd);
+ case DC_STATE_HANDSHAKE:
+ if (!msn_dc_verify_handshake(dc, packet_length))
+ return DC_PROCESS_FALLBACK;
- msn_directconn_destroy(directconn);
+ msn_dc_send_handshake_reply(dc);
+ dc->state = DC_STATE_ESTABLISHED;
+
+ msn_slpcall_session_init(dc->slpcall);
+ dc->slpcall = NULL;
+ break;
+
+ case DC_STATE_HANDSHAKE_REPLY:
+ if (!msn_dc_verify_handshake(dc, packet_length))
+ return DC_PROCESS_FALLBACK;
+
+ dc->state = DC_STATE_ESTABLISHED;
+
+ msn_slpcall_session_init(dc->slpcall);
+ dc->slpcall = NULL;
+ break;
+
+ case DC_STATE_ESTABLISHED:
+ msn_slplink_process_msg(
+ dc->slplink,
+ &dc->header,
+ dc->in_buffer + 4 + DC_PACKET_HEADER_SIZE,
+ dc->header.length
+ );
+
+ /*
+ if (dc->num_calls == 0) {
+ msn_dc_destroy(dc);
+
+ return DC_PROCESS_CLOSE;
+ }
+ */
+ break;
}
- g_free(body);
+ return DC_PROCESS_OK;
}
static void
-connect_cb(gpointer data, gint source, PurpleInputCondition cond)
+msn_dc_recv_cb(gpointer data, gint fd, PurpleInputCondition cond)
{
- MsnDirectConn* directconn;
- int fd;
+ MsnDirectConn *dc;
+ int free_buf_space;
+ int bytes_received;
+ guint32 packet_length;
- purple_debug_misc("msn", "directconn: connect_cb: %d, %d.\n", source, cond);
+ g_return_if_fail(data != NULL);
+ g_return_if_fail(fd != -1);
- directconn = data;
- directconn->connect_data = NULL;
+ dc = data;
+ free_buf_space = dc->in_size - dc->in_pos;
- if (TRUE)
- {
- fd = source;
- }
- else
- {
- struct sockaddr_in client_addr;
- socklen_t client;
- fd = accept (source, (struct sockaddr *)&client_addr, &client);
+ bytes_received = recv(fd, dc->in_buffer + dc->in_pos, free_buf_space, 0);
+ if (bytes_received < 0) {
+ if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
+ return;
+
+ purple_debug_warning("msn", "msn_dc_recv_cb: recv error\n");
+
+ if(dc->state != DC_STATE_ESTABLISHED)
+ msn_dc_fallback_to_sb(dc);
+ else
+ msn_dc_destroy(dc);
+ return;
+
+ } else if (bytes_received == 0) {
+ /* EOF. Remote side closed connection. */
+ purple_debug_info("msn", "msn_dc_recv_cb: recv EOF\n");
+
+ if(dc->state != DC_STATE_ESTABLISHED)
+ msn_dc_fallback_to_sb(dc);
+ else
+ msn_dc_destroy(dc);
+ return;
}
- directconn->fd = fd;
+ dc->progress = TRUE;
- if (fd > 0)
- {
- directconn->inpa = purple_input_add(fd, PURPLE_INPUT_READ, read_cb,
- directconn);
+ dc->in_pos += bytes_received;
- if (TRUE)
- {
- /* Send foo. */
- msn_directconn_write(directconn, "foo", strlen("foo") + 1);
+ /* Wait for packet length */
+ while (dc->in_pos >= 4) {
+ packet_length = GUINT32_FROM_LE(*((guint32*)dc->in_buffer));
- /* Send Handshake */
- msn_directconn_send_handshake(directconn);
+ if (packet_length > DC_MAX_PACKET_SIZE) {
+ /* Oversized packet */
+ purple_debug_warning("msn", "msn_dc_recv_cb: oversized packet received\n");
+ return;
}
- else
- {
+
+ /* Wait for the whole packet to arrive */
+ if (dc->in_pos < 4 + packet_length)
+ return;
+
+ if (dc->state != DC_STATE_FOO) {
+ msn_dc_parse_binary_header(dc);
}
+
+ switch (msn_dc_process_packet(dc, packet_length)) {
+ case DC_PROCESS_CLOSE:
+ return;
+
+ case DC_PROCESS_FALLBACK:
+ purple_debug_warning("msn", "msn_dc_recv_cb: packet processing error, fall back to SB\n");
+ msn_dc_fallback_to_sb(dc);
+ return;
+
+ }
+
+ if (dc->in_pos > packet_length + 4) {
+ g_memmove(dc->in_buffer, dc->in_buffer + 4 + packet_length, dc->in_pos - packet_length - 4);
+ }
+
+ dc->in_pos -= packet_length + 4;
}
- else
- {
- /* ERROR */
- purple_debug_error("msn", "could not add input\n");
+}
+
+static gboolean
+msn_dc_timeout(gpointer data)
+{
+ MsnDirectConn *dc = data;
- if (directconn->inpa)
- purple_input_remove(directconn->inpa);
+ g_return_val_if_fail(dc != NULL, FALSE);
- close(directconn->fd);
+ if (dc->progress) {
+ dc->progress = FALSE;
+ return TRUE;
+ } else {
+ dc->timeout_handle = 0;
+ msn_dc_destroy(dc);
+ return FALSE;
}
}
static void
-directconn_connect_cb(gpointer data, gint source, const gchar *error_message)
+msn_dc_init(MsnDirectConn *dc)
{
- if (error_message)
- purple_debug_error("msn", "Error making direct connection: %s\n", error_message);
+ g_return_if_fail(dc != NULL);
+
+ dc->in_size = DC_MAX_PACKET_SIZE + 4;
+ dc->in_pos = 0;
+ dc->in_buffer = g_malloc(dc->in_size);
+
+ dc->recv_handle = purple_input_add(dc->fd, PURPLE_INPUT_READ, msn_dc_recv_cb, dc);
+ dc->send_handle = purple_input_add(dc->fd, PURPLE_INPUT_WRITE, msn_dc_send_cb, dc);
- connect_cb(data, source, PURPLE_INPUT_READ);
+ dc->timeout_handle = purple_timeout_add_seconds(DC_TIMEOUT, msn_dc_timeout, dc);
}
-gboolean
-msn_directconn_connect(MsnDirectConn *directconn, const char *host, int port)
+void
+msn_dc_connected_to_peer_cb(gpointer data, gint fd, const gchar *error_msg)
{
- MsnSession *session;
+ MsnDirectConn *dc = data;
+
+ if (purple_debug_is_verbose())
+ purple_debug_info("msn", "msn_dc_connected_to_peer_cb %p\n", dc);
- g_return_val_if_fail(directconn != NULL, FALSE);
- g_return_val_if_fail(host != NULL, TRUE);
- g_return_val_if_fail(port > 0, FALSE);
+ g_return_if_fail(dc != NULL);
- session = directconn->slplink->session;
+ dc->connect_data = NULL;
+ purple_timeout_remove(dc->connect_timeout_handle);
+ dc->connect_timeout_handle = 0;
-#if 0
- if (session->http_method)
- {
- servconn->http_data->gateway_host = g_strdup(host);
+ dc->fd = fd;
+ if (dc->fd != -1) {
+ msn_dc_init(dc);
+ msn_dc_send_foo(dc);
+ msn_dc_send_handshake(dc);
+ dc->state = DC_STATE_HANDSHAKE_REPLY;
}
-#endif
+}
- directconn->connect_data = purple_proxy_connect(NULL, session->account,
- host, port, directconn_connect_cb, directconn);
+/*
+ * This callback will be called when we're the server
+ * and nobody has connected us in DC_INCOMING_TIMEOUT seconds
+ */
+static gboolean
+msn_dc_incoming_connection_timeout_cb(gpointer data) {
+ MsnDirectConn *dc = data;
- return (directconn->connect_data != NULL);
-}
+ if (purple_debug_is_verbose())
+ purple_debug_info("msn", "msn_dc_incoming_connection_timeout_cb %p\n", dc);
-void
-msn_directconn_listen(MsnDirectConn *directconn)
-{
- int port;
- int fd;
+ g_return_val_if_fail(dc != NULL, FALSE);
- port = 7000;
+ if (dc->listen_data != NULL) {
+ purple_network_listen_cancel(dc->listen_data);
+ dc->listen_data = NULL;
+ }
- for (fd = -1; fd < 0;)
- fd = create_listener(++port);
+ if (dc->listenfd_handle != 0) {
+ purple_input_remove(dc->listenfd_handle);
+ dc->listenfd_handle = 0;
+ }
- directconn->fd = fd;
+ if (dc->listenfd != -1) {
+ purple_network_remove_port_mapping(dc->listenfd);
+ close(dc->listenfd);
+ dc->listenfd = -1;
+ }
- directconn->inpa = purple_input_add(fd, PURPLE_INPUT_READ, connect_cb,
- directconn);
+ dc->connect_timeout_handle = 0;
+ msn_dc_fallback_to_sb(dc);
- directconn->port = port;
- directconn->c = 0;
+ return FALSE;
}
-MsnDirectConn*
-msn_directconn_new(MsnSlpLink *slplink)
+/*
+ * This callback will be called when we're unable to connect to
+ * the remote host in DC_OUTGOING_TIMEOUT seconds.
+ */
+gboolean
+msn_dc_outgoing_connection_timeout_cb(gpointer data)
{
- MsnDirectConn *directconn;
+ MsnDirectConn *dc = data;
- directconn = g_new0(MsnDirectConn, 1);
+ purple_debug_info("msn", "msn_dc_outgoing_connection_timeout_cb %p\n", dc);
- directconn->slplink = slplink;
+ g_return_val_if_fail(dc != NULL, FALSE);
- if (slplink->directconn != NULL)
- purple_debug_info("msn", "got_transresp: LEAK\n");
+ dc->connect_timeout_handle = 0;
- slplink->directconn = directconn;
+ if (dc->connect_data != NULL) {
+ purple_proxy_connect_cancel(dc->connect_data);
+ dc->connect_data = NULL;
+ }
- return directconn;
+ if (dc->ext_ip && dc->ext_port) {
+ /* Try external IP/port if available. */
+ dc->connect_data = purple_proxy_connect(
+ NULL,
+ dc->slpcall->slplink->session->account,
+ dc->ext_ip,
+ dc->ext_port,
+ msn_dc_connected_to_peer_cb,
+ dc
+ );
+
+ g_free(dc->ext_ip);
+ dc->ext_ip = NULL;
+
+ if (dc->connect_data) {
+ dc->connect_timeout_handle = purple_timeout_add_seconds(
+ DC_OUTGOING_TIMEOUT,
+ msn_dc_outgoing_connection_timeout_cb,
+ dc
+ );
+ } else {
+ /*
+ * Connection failed
+ * Fall back to SB transfer
+ */
+ msn_dc_outgoing_connection_timeout_cb(dc);
+ }
+
+ } else {
+ /*
+ * Both internal and external connection attempts failed.
+ * Fall back to SB transfer.
+ */
+ msn_dc_fallback_to_sb(dc);
+ }
+
+ return FALSE;
}
-void
-msn_directconn_destroy(MsnDirectConn *directconn)
+/*
+ * This callback will be called when we're the server
+ * and somebody has connected to us in DC_INCOMING_TIMEOUT seconds.
+ */
+static void
+msn_dc_incoming_connection_cb(gpointer data, gint listenfd, PurpleInputCondition cond)
{
- if (directconn->connect_data != NULL)
- purple_proxy_connect_cancel(directconn->connect_data);
+ MsnDirectConn *dc = data;
+
+ if (purple_debug_is_verbose())
+ purple_debug_info("msn", "msn_dc_incoming_connection_cb %p\n", dc);
+
+ g_return_if_fail(dc != NULL);
+
+ if (dc->connect_timeout_handle != 0) {
+ purple_timeout_remove(dc->connect_timeout_handle);
+ dc->connect_timeout_handle = 0;
+ }
- if (directconn->inpa != 0)
- purple_input_remove(directconn->inpa);
+ if (dc->listenfd_handle != 0) {
+ purple_input_remove(dc->listenfd_handle);
+ dc->listenfd_handle = 0;
+ }
- if (directconn->fd >= 0)
- close(directconn->fd);
+ dc->fd = accept(listenfd, NULL, 0);
- if (directconn->nonce != NULL)
- g_free(directconn->nonce);
+ purple_network_remove_port_mapping(dc->listenfd);
+ close(dc->listenfd);
+ dc->listenfd = -1;
- directconn->slplink->directconn = NULL;
+ if (dc->fd != -1) {
+ msn_dc_init(dc);
+ dc->state = DC_STATE_FOO;
+ }
+}
- g_free(directconn);
+void
+msn_dc_listen_socket_created_cb(int listenfd, gpointer data)
+{
+ MsnDirectConn *dc = data;
+
+ if (purple_debug_is_verbose())
+ purple_debug_info("msn", "msn_dc_listen_socket_created_cb %p\n", dc);
+
+ g_return_if_fail(dc != NULL);
+
+ dc->listen_data = NULL;
+
+ if (listenfd != -1) {
+ const char *ext_ip;
+ const char *int_ip;
+ int port;
+
+ ext_ip = purple_network_get_my_ip(listenfd);
+ int_ip = purple_network_get_local_system_ip(listenfd);
+ port = purple_network_get_port_from_fd(listenfd);
+
+ dc->listenfd = listenfd;
+ dc->listenfd_handle = purple_input_add(
+ listenfd,
+ PURPLE_INPUT_READ,
+ msn_dc_incoming_connection_cb,
+ dc
+ );
+ dc->connect_timeout_handle = purple_timeout_add_seconds(
+ DC_INCOMING_TIMEOUT,
+ msn_dc_incoming_connection_timeout_cb,
+ dc
+ );
+
+ if (strcmp(int_ip, ext_ip) != 0) {
+ dc->msg_body = g_strdup_printf(
+ "Bridge: TCPv1\r\n"
+ "Listening: true\r\n"
+ "%sNonce: {%s}\r\n"
+ "IPv4External-Addrs: %s\r\n"
+ "IPv4External-Port: %d\r\n"
+ "IPv4Internal-Addrs: %s\r\n"
+ "IPv4Internal-Port: %d\r\n"
+ "\r\n",
+
+ dc->nonce_type != DC_NONCE_PLAIN ? "Hashed-" : "",
+ dc->nonce_hash,
+ ext_ip,
+ port,
+ int_ip,
+ port
+ );
+
+ } else {
+ dc->msg_body = g_strdup_printf(
+ "Bridge: TCPv1\r\n"
+ "Listening: true\r\n"
+ "%sNonce: {%s}\r\n"
+ "IPv4External-Addrs: %s\r\n"
+ "IPv4External-Port: %d\r\n"
+ "\r\n",
+
+ dc->nonce_type != DC_NONCE_PLAIN ? "Hashed-" : "",
+ dc->nonce_hash,
+ ext_ip,
+ port
+ );
+ }
+
+ if (dc->slpcall->wait_for_socket) {
+ if (dc->send_connection_info_msg_cb != NULL)
+ dc->send_connection_info_msg_cb(dc);
+
+ dc->slpcall->wait_for_socket = FALSE;
+ }
+ }
}
+
diff --git a/libpurple/protocols/msn/directconn.h b/libpurple/protocols/msn/directconn.h
index f87c4b2153..2aae22ec69 100644
--- a/libpurple/protocols/msn/directconn.h
+++ b/libpurple/protocols/msn/directconn.h
@@ -26,36 +26,174 @@
typedef struct _MsnDirectConn MsnDirectConn;
+#include "network.h"
+#include "proxy.h"
+#include "circbuffer.h"
+
#include "msg.h"
#include "slp.h"
#include "slplink.h"
+#include "slpmsg.h"
+
+typedef enum
+{
+ DC_STATE_CLOSED, /*< No socket opened yet */
+ DC_STATE_FOO, /*< Waiting for FOO message */
+ DC_STATE_HANDSHAKE, /*< Waiting for handshake message */
+ DC_STATE_HANDSHAKE_REPLY, /*< Waiting for handshake reply message */
+ DC_STATE_ESTABLISHED /*< Handshake complete */
+} MsnDirectConnState;
+
+typedef enum
+{
+ DC_PROCESS_OK = 0,
+ DC_PROCESS_ERROR,
+ DC_PROCESS_FALLBACK,
+ DC_PROCESS_CLOSE
+
+} MsnDirectConnProcessResult;
+
+typedef enum
+{
+ DC_NONCE_UNKNOWN, /**< Invalid scheme */
+ DC_NONCE_PLAIN, /**< No hashing */
+ DC_NONCE_SHA1 /**< First 16 bytes of SHA1 of nonce */
+
+} MsnDirectConnNonceType;
+
+typedef struct _MsnDirectConnPacket MsnDirectConnPacket;
+
+struct _MsnDirectConnPacket {
+ guint32 length;
+ guchar *data;
+
+ void (*sent_cb)(struct _MsnDirectConnPacket*);
+ MsnMessage *msg;
+};
struct _MsnDirectConn
{
- MsnSlpLink *slplink;
- MsnSlpCall *initial_call;
+ MsnDirectConnState state; /**< Direct connection status */
+ MsnSlpLink *slplink; /**< The slplink using this direct connection */
+ MsnSlpCall *slpcall; /**< The slpcall which initiated the direct connection */
+ char *msg_body; /**< The body of message sent by send_connection_info_msg_cb */
+ MsnSlpMessage *prev_ack; /**< The saved SLP ACK message */
+
+ MsnDirectConnNonceType nonce_type; /**< The type of nonce hashing */
+ guchar nonce[16]; /**< The nonce used for handshake */
+ gchar nonce_hash[37]; /**< The hash of nonce */
+ gchar remote_nonce[37]; /**< The remote side's nonce */
+
+ PurpleNetworkListenData *listen_data; /**< The pending socket creation request */
+ PurpleProxyConnectData *connect_data; /**< The pending connection attempt */
+ int listenfd; /**< The socket we're listening for incoming connections */
+ guint listenfd_handle; /**< The timeout handle for incoming connection */
+ guint connect_timeout_handle; /**< The timeout handle for outgoing connection */
- PurpleProxyConnectData *connect_data;
+ int fd; /**< The direct connection socket */
+ guint recv_handle; /**< The incoming data callback handle */
+ guint send_handle; /**< The outgoing data callback handle */
- gboolean acked;
+ gchar *in_buffer; /**< The receive buffer */
+ int in_size; /**< The receive buffer size */
+ int in_pos; /**< The first free position in receive buffer */
+ GQueue *out_queue; /**< The outgoing packet queue */
+ int msg_pos; /**< The position of next byte to be sent in the actual packet */
- char *nonce;
+ MsnSlpHeader header; /**< SLP header for parsing / serializing */
- int fd;
+ /** The callback used for sending information to the peer about the opened socket */
+ void (*send_connection_info_msg_cb)(MsnDirectConn *);
- int port;
- int inpa;
+ gchar *ext_ip; /**< Our external IP address */
+ int ext_port; /**< Our external port */
- int c;
+ guint timeout_handle;
+ gboolean progress;
+
+ /*int num_calls;*/ /**< The number of slpcalls using this direct connection */
};
-MsnDirectConn *msn_directconn_new(MsnSlpLink *slplink);
-gboolean msn_directconn_connect(MsnDirectConn *directconn,
- const char *host, int port);
-void msn_directconn_listen(MsnDirectConn *directconn);
-void msn_directconn_send_msg(MsnDirectConn *directconn, MsnMessage *msg);
-void msn_directconn_parse_nonce(MsnDirectConn *directconn, const char *nonce);
-void msn_directconn_destroy(MsnDirectConn *directconn);
-void msn_directconn_send_handshake(MsnDirectConn *directconn);
+/* Outgoing attempt */
+#define DC_OUTGOING_TIMEOUT (5)
+/* Time for internal + external connection attempts */
+#define DC_INCOMING_TIMEOUT (DC_OUTGOING_TIMEOUT * 3)
+/* Timeout for lack of activity */
+#define DC_TIMEOUT (60)
+
+/*
+ * Queues an MSN message to be sent via direct connection.
+ */
+void
+msn_dc_enqueue_msg(MsnDirectConn *dc, MsnMessage *msg);
+
+/*
+ * Creates, initializes, and returns a new MsnDirectConn structure.
+ */
+MsnDirectConn *
+msn_dc_new(MsnSlpCall *slpcall);
+
+/*
+ * Destroys an MsnDirectConn structure. Frees every buffer allocated earlier
+ * restores saved callbacks, etc.
+ */
+void
+msn_dc_destroy(MsnDirectConn *dc);
+
+/*
+ * Fallback to switchboard connection. Used when neither side is able to
+ * create a listening socket.
+ */
+void
+msn_dc_fallback_to_sb(MsnDirectConn *dc);
+
+/*
+ * Increases the slpcall counter in DC. The direct connection remains open
+ * until all slpcalls using it are destroyed.
+ */
+void
+msn_dc_ref(MsnDirectConn *dc);
+
+/*
+ * Decrease the slpcall counter in DC. The direct connection remains open
+ * until all slpcalls using it are destroyed.
+ */
+void
+msn_dc_unref(MsnDirectConn *dc);
+
+/*
+ * Sends a direct connect INVITE message on the associated slplink
+ * with the corresponding connection type and information.
+ */
+void
+msn_dc_send_invite(MsnDirectConn *dc);
+
+/*
+ * Sends a direct connect OK message as a response to an INVITE received earliaer
+ * on the corresponding slplink.
+ */
+void
+msn_dc_send_ok(MsnDirectConn *dc);
+
+/*
+ * This callback will be called when we're successfully connected to
+ * the remote host.
+ */
+void
+msn_dc_connected_to_peer_cb(gpointer data, gint fd, const gchar *error_msg);
+
+/*
+ * This callback will be called when we're unable to connect to
+ * the remote host in DC_CONNECT_TIMEOUT seconds.
+ */
+gboolean
+msn_dc_outgoing_connection_timeout_cb(gpointer data);
+
+/*
+ * This callback will be called when the listening socket is successfully
+ * created and its parameters (IP/port) are available.
+ */
+void
+msn_dc_listen_socket_created_cb(int listenfd, gpointer data);
#endif /* MSN_DIRECTCONN_H */
diff --git a/libpurple/protocols/msn/httpconn.c b/libpurple/protocols/msn/httpconn.c
index ba44841091..708cbcc362 100644
--- a/libpurple/protocols/msn/httpconn.c
+++ b/libpurple/protocols/msn/httpconn.c
@@ -239,6 +239,9 @@ msn_httpconn_parse_data(MsnHttpConn *httpconn, const char *buf,
}
else
{
+ /* I'll be honest, I don't fully understand all this, but this
+ * causes crashes, Stu. */
+#if 0
MsnServConn *servconn;
/* It's going to die. */
@@ -246,10 +249,9 @@ msn_httpconn_parse_data(MsnHttpConn *httpconn, const char *buf,
servconn = httpconn->servconn;
- /* I'll be honest, I don't fully understand all this, but this
- * causes crashes, Stu. */
- /* if (servconn != NULL)
- servconn->wasted = TRUE; */
+ if (servconn != NULL)
+ servconn->wasted = TRUE;
+#endif
g_free(full_session_id);
g_free(session_id);
diff --git a/libpurple/protocols/msn/msg.c b/libpurple/protocols/msn/msg.c
index 9e5f16ef4b..57c4aba706 100644
--- a/libpurple/protocols/msn/msg.c
+++ b/libpurple/protocols/msn/msg.c
@@ -939,8 +939,11 @@ datacast_inform_user(MsnSwitchBoard *swboard, const char *who,
char *username, *str;
PurpleAccount *account;
PurpleBuddy *b;
+ PurpleConnection *pc;
+ gboolean chat;
account = swboard->session->account;
+ pc = purple_account_get_connection(account);
if ((b = purple_find_buddy(account, who)) != NULL)
username = g_markup_escape_text(purple_buddy_get_alias(b), -1);
@@ -949,8 +952,14 @@ datacast_inform_user(MsnSwitchBoard *swboard, const char *who,
str = g_strdup_printf(msg, username, filename);
g_free(username);
+ swboard->flag |= MSN_SB_FLAG_IM;
+ if (swboard->current_users > 1)
+ chat = TRUE;
+ else
+ chat = FALSE;
+
if (swboard->conv == NULL) {
- if (swboard->current_users > 1)
+ if (chat)
swboard->conv = purple_find_chat(account->gc, swboard->chat_id);
else {
swboard->conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
@@ -959,9 +968,15 @@ datacast_inform_user(MsnSwitchBoard *swboard, const char *who,
swboard->conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, who);
}
}
- swboard->flag |= MSN_SB_FLAG_IM;
- purple_conversation_write(swboard->conv, NULL, str, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ if (chat)
+ serv_got_chat_in(pc,
+ purple_conv_chat_get_id(PURPLE_CONV_CHAT(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,
+ time(NULL));
g_free(str);
}
@@ -970,14 +985,13 @@ datacast_inform_user(MsnSwitchBoard *swboard, const char *who,
static void
got_wink_cb(MsnSlpCall *slpcall, const guchar *data, gsize size)
{
- FILE *f;
+ FILE *f = NULL;
char *path = NULL;
const char *who = slpcall->slplink->remote_user;
purple_debug_info("msn", "Received wink from %s\n", who);
- if ((f = purple_mkstemp(&path, TRUE))) {
- fwrite(data, size, 1, f);
- fclose(f);
+ if ((f = purple_mkstemp(&path, TRUE)) &&
+ (fwrite(data, 1, size, f) == size)) {
datacast_inform_user(slpcall->slplink->swboard,
who,
_("%s sent a wink. <a href='msn-wink://%s'>Click here to play it</a>"),
@@ -988,21 +1002,22 @@ got_wink_cb(MsnSlpCall *slpcall, const guchar *data, gsize size)
who,
_("%s sent a wink, but it could not be saved"),
NULL);
- }
+ }
+ if (f)
+ fclose(f);
g_free(path);
}
static void
got_voiceclip_cb(MsnSlpCall *slpcall, const guchar *data, gsize size)
{
- FILE *f;
+ FILE *f = NULL;
char *path = NULL;
const char *who = slpcall->slplink->remote_user;
purple_debug_info("msn", "Received voice clip from %s\n", who);
- if ((f = purple_mkstemp(&path, TRUE))) {
- fwrite(data, size, 1, f);
- fclose(f);
+ if ((f = purple_mkstemp(&path, TRUE)) &&
+ (fwrite(data, 1, size, f) == size)) {
datacast_inform_user(slpcall->slplink->swboard,
who,
_("%s sent a voice clip. <a href='audio://%s'>Click here to play it</a>"),
@@ -1013,7 +1028,9 @@ got_voiceclip_cb(MsnSlpCall *slpcall, const guchar *data, gsize size)
who,
_("%s sent a voice clip, but it could not be saved"),
NULL);
- }
+ }
+ if (f)
+ fclose(f);
g_free(path);
}
@@ -1100,7 +1117,8 @@ void
msn_invite_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
{
GHashTable *body;
- const gchar *guid;
+ const gchar *command;
+ const gchar *cookie;
gboolean accepted = FALSE;
g_return_if_fail(cmdproc != NULL);
@@ -1113,59 +1131,64 @@ msn_invite_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
"Unable to parse invite msg body.\n");
return;
}
+
+ /*
+ * GUID is NOT always present but Invitation-Command and Invitation-Cookie
+ * are mandatory.
+ */
+ command = g_hash_table_lookup(body, "Invitation-Command");
+ cookie = g_hash_table_lookup(body, "Invitation-Cookie");
+
+ if (command == NULL || cookie == NULL) {
+ purple_debug_warning("msn",
+ "Invalid invitation message: either Invitation-Command "
+ "or Invitation-Cookie is missing or invalid.\n"
+ );
+ return;
- guid = g_hash_table_lookup(body, "Application-GUID");
-
- if (guid == NULL) {
- const gchar *cmd = g_hash_table_lookup(
- body, "Invitation-Command");
-
- if (cmd && !strcmp(cmd, "CANCEL")) {
- const gchar *code = g_hash_table_lookup(
- body, "Cancel-Code");
- purple_debug_info("msn",
- "MSMSGS invitation cancelled: %s.\n",
- code ? code : "no reason given");
- } else
- purple_debug_warning("msn", "Invite msg missing "
- "Application-GUID.\n");
-
- accepted = TRUE;
-
- } else if (!strcmp(guid, "{02D3C01F-BF30-4825-A83A-DE7AF41648AA}")) {
- purple_debug_info("msn", "Computer call\n");
-
- if (cmdproc->session) {
- PurpleConversation *conv = 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)
- 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));
- g_free(buf);
+ } else if (!strcmp(command, "INVITE")) {
+ const gchar *guid = g_hash_table_lookup(body, "Application-GUID");
+
+ if (guid == NULL) {
+ purple_debug_warning("msn",
+ "Invite msg missing Application-GUID.\n");
+
+ accepted = TRUE;
+
+ } else if (!strcmp(guid, MSN_FT_GUID)) {
+
+ } else if (!strcmp(guid, "{02D3C01F-BF30-4825-A83A-DE7AF41648AA}")) {
+ purple_debug_info("msn", "Computer call\n");
+
+ if (cmdproc->session) {
+ PurpleConversation *conv = 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)
+ 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));
+ g_free(buf);
+ }
}
+ } else {
+ const gchar *application = g_hash_table_lookup(body, "Application-Name");
+ purple_debug_warning("msn", "Unhandled invite msg with GUID %s: %s.\n",
+ guid, application ? application : "(null)");
}
- } else {
- const gchar *application = g_hash_table_lookup(body, "Application-Name");
- purple_debug_warning("msn", "Unhandled invite msg with GUID %s: %s.\n",
- guid, application ? application : "(null)");
- }
-
- if (!accepted) {
- const gchar *cookie = g_hash_table_lookup(body, "Invitation-Cookie");
- if (cookie) {
+
+ if (!accepted) {
MsnSwitchBoard *swboard = cmdproc->data;
char *text;
MsnMessage *cancel;
@@ -1185,6 +1208,17 @@ msn_invite_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
msn_switchboard_send_msg(swboard, cancel, TRUE);
msn_message_destroy(cancel);
}
+
+ } else if (!strcmp(command, "CANCEL")) {
+ const gchar *code = g_hash_table_lookup(body, "Cancel-Code");
+ purple_debug_info("msn", "MSMSGS invitation cancelled: %s.\n",
+ code ? code : "no reason given");
+
+ } else {
+ /*
+ * Some other already established invitation session.
+ * Can be retrieved by Invitation-Cookie.
+ */
}
g_hash_table_destroy(body);
diff --git a/libpurple/protocols/msn/msn.c b/libpurple/protocols/msn/msn.c
index c20f31886f..93e1db7337 100644
--- a/libpurple/protocols/msn/msn.c
+++ b/libpurple/protocols/msn/msn.c
@@ -195,39 +195,164 @@ msn_cmd_nudge(PurpleConversation *conv, const gchar *cmd, gchar **args, gchar **
return PURPLE_CMD_RET_OK;
}
+struct public_alias_closure
+{
+ PurpleAccount *account;
+ gpointer success_cb;
+ gpointer failure_cb;
+};
+
+static gboolean
+set_public_alias_length_error(gpointer data)
+{
+ struct public_alias_closure *closure = data;
+ PurpleSetPublicAliasFailureCallback failure_cb = closure->failure_cb;
+
+ failure_cb(closure->account, _("Your new MSN friendly name is too long."));
+ g_free(closure);
+
+ return FALSE;
+}
+
+static void
+prp_success_cb(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+ const char *type, *friendlyname;
+ struct public_alias_closure *closure;
+
+ g_return_if_fail(cmd->param_count >= 3);
+ type = cmd->params[1];
+ g_return_if_fail(!strcmp(type, "MFN"));
+
+ closure = cmd->trans->data;
+ friendlyname = purple_url_decode(cmd->params[2]);
+
+ msn_update_contact(cmdproc->session, "Me", MSN_UPDATE_DISPLAY, friendlyname);
+
+ purple_connection_set_display_name(
+ purple_account_get_connection(closure->account),
+ friendlyname);
+ purple_account_set_string(closure->account, "display-name", friendlyname);
+
+ if (closure->success_cb) {
+ PurpleSetPublicAliasSuccessCallback success_cb = closure->success_cb;
+ success_cb(closure->account, friendlyname);
+ }
+}
+
+static void
+prp_error_cb(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
+{
+ struct public_alias_closure *closure = trans->data;
+ PurpleSetPublicAliasFailureCallback failure_cb = closure->failure_cb;
+ gboolean debug;
+ const char *error_text;
+
+ error_text = msn_error_get_text(error, &debug);
+ failure_cb(closure->account, error_text);
+}
+
+static void
+prp_timeout_cb(MsnCmdProc *cmdproc, MsnTransaction *trans)
+{
+ struct public_alias_closure *closure = trans->data;
+ PurpleSetPublicAliasFailureCallback failure_cb = closure->failure_cb;
+ failure_cb(closure->account, _("Connection Timeout"));
+}
+
void
-msn_act_id(PurpleConnection *gc, const char *entry)
+msn_set_public_alias(PurpleConnection *pc, const char *alias,
+ PurpleSetPublicAliasSuccessCallback success_cb,
+ PurpleSetPublicAliasFailureCallback failure_cb)
{
MsnCmdProc *cmdproc;
MsnSession *session;
PurpleAccount *account;
- const char *alias;
+ const char *real_alias;
+ MsnTransaction *trans;
+ struct public_alias_closure *closure;
- session = gc->proto_data;
+ session = purple_connection_get_protocol_data(pc);
cmdproc = session->notification->cmdproc;
- account = purple_connection_get_account(gc);
+ account = purple_connection_get_account(pc);
- if (entry && *entry)
+ if (alias && *alias)
{
- char *tmp = g_strdup(entry);
- alias = purple_url_encode(g_strstrip(tmp));
+ char *tmp = g_strdup(alias);
+ real_alias = purple_url_encode(g_strstrip(tmp));
g_free(tmp);
}
else
- alias = "";
+ real_alias = "";
- if (strlen(alias) > BUDDY_ALIAS_MAXLEN)
+ if (strlen(real_alias) > BUDDY_ALIAS_MAXLEN)
{
- purple_notify_error(gc, NULL,
- _("Your new MSN friendly name is too long."), NULL);
+ if (failure_cb) {
+ struct public_alias_closure *closure =
+ g_new0(struct public_alias_closure, 1);
+ closure->account = 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);
+ }
return;
}
- if (*alias == '\0') {
- alias = purple_url_encode(purple_account_get_username(account));
+ if (*real_alias == '\0') {
+ real_alias = purple_url_encode(purple_account_get_username(account));
}
- msn_cmdproc_send(cmdproc, "PRP", "MFN %s", alias);
+ closure = g_new0(struct public_alias_closure, 1);
+ closure->account = account;
+ closure->success_cb = success_cb;
+ closure->failure_cb = failure_cb;
+
+ trans = msn_transaction_new(cmdproc, "PRP", "MFN %s", real_alias);
+ msn_transaction_set_data(trans, closure);
+ msn_transaction_set_data_free(trans, g_free);
+ msn_transaction_add_cb(trans, "PRP", prp_success_cb);
+ if (failure_cb) {
+ msn_transaction_set_error_cb(trans, prp_error_cb);
+ msn_transaction_set_timeout_cb(trans, prp_timeout_cb);
+ }
+ msn_cmdproc_send_trans(cmdproc, trans);
+}
+
+static gboolean
+get_public_alias_cb(gpointer data)
+{
+ struct public_alias_closure *closure = data;
+ PurpleGetPublicAliasSuccessCallback success_cb = closure->success_cb;
+ const char *alias;
+
+ alias = purple_account_get_string(closure->account, "display-name",
+ purple_account_get_username(closure->account));
+ success_cb(closure->account, alias);
+ g_free(closure);
+
+ return FALSE;
+}
+
+static void
+msn_get_public_alias(PurpleConnection *pc,
+ PurpleGetPublicAliasSuccessCallback success_cb,
+ PurpleGetPublicAliasFailureCallback failure_cb)
+{
+ struct public_alias_closure *closure = g_new0(struct public_alias_closure, 1);
+ PurpleAccount *account = purple_connection_get_account(pc);
+
+ closure->account = account;
+ closure->success_cb = success_cb;
+ purple_timeout_add(0, get_public_alias_cb, closure);
+}
+
+static void
+msn_act_id(PurpleConnection *gc, const char *entry)
+{
+ msn_set_public_alias(gc, entry, NULL, NULL);
}
static void
@@ -497,7 +622,6 @@ show_send_to_mobile_cb(PurpleBlistNode *node, gpointer ignored)
{
PurpleBuddy *buddy;
PurpleConnection *gc;
- MsnSession *session;
MsnMobileData *data;
PurpleAccount *account;
const char *name;
@@ -509,8 +633,6 @@ show_send_to_mobile_cb(PurpleBlistNode *node, gpointer ignored)
gc = purple_account_get_connection(account);
name = purple_buddy_get_name(buddy);
- session = gc->proto_data;
-
data = g_new0(MsnMobileData, 1);
data->gc = gc;
data->passport = name;
@@ -589,6 +711,14 @@ t_msn_xfer_init(PurpleXfer *xfer)
{
MsnSlpLink *slplink = xfer->data;
msn_slplink_request_ft(slplink, xfer);
+ msn_slplink_unref(slplink);
+}
+
+static void
+t_msn_xfer_cancel_send(PurpleXfer *xfer)
+{
+ MsnSlpLink *slplink = xfer->data;
+ msn_slplink_unref(slplink);
}
static PurpleXfer*
@@ -603,9 +733,10 @@ msn_new_xfer(PurpleConnection *gc, const char *who)
g_return_val_if_fail(xfer != NULL, NULL);
- xfer->data = msn_session_get_slplink(session, who);
+ xfer->data = 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);
return xfer;
}
@@ -1950,11 +2081,9 @@ static void
msn_remove_group(PurpleConnection *gc, PurpleGroup *group)
{
MsnSession *session;
- MsnCmdProc *cmdproc;
const char *gname;
session = gc->proto_data;
- cmdproc = session->notification->cmdproc;
gname = purple_group_get_name(group);
purple_debug_info("msn", "Remove group %s\n", gname);
@@ -2070,6 +2199,7 @@ msn_got_info(PurpleUtilFetchUrlData *url_data, gpointer data,
const gchar *url_text, size_t len, const gchar *error_message)
{
MsnGetInfoData *info_data = (MsnGetInfoData *)data;
+ MsnSession *session;
PurpleNotifyUserInfo *user_info;
char *stripped, *p, *q, *tmp;
char *user_url = NULL;
@@ -2087,15 +2217,8 @@ msn_got_info(PurpleUtilFetchUrlData *url_data, gpointer data,
purple_debug_info("msn", "In msn_got_info,url_text:{%s}\n",url_text);
- /* Make sure the connection is still valid */
- /* TODO: Instead of this, we should be canceling this when we disconnect */
- if (g_list_find(purple_connections_get_all(), info_data->gc) == NULL)
- {
- purple_debug_warning("msn", "invalid connection. ignoring buddy info.\n");
- g_free(info_data->name);
- g_free(info_data);
- return;
- }
+ 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);
@@ -2480,13 +2603,14 @@ msn_got_info(PurpleUtilFetchUrlData *url_data, gpointer data,
/* Try to put the photo in there too, if there's one */
if (photo_url_text)
{
- purple_util_fetch_url_len(photo_url_text, FALSE, NULL, FALSE, MAX_HTTP_BUDDYICON_BYTES, msn_got_photo,
- info2_data);
+ 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);
}
else
{
- /* Emulate a callback */
- /* TODO: Huh? */
+ /* Finish the Get Info and show the user something */
msn_got_photo(NULL, info2_data, NULL, 0, NULL);
}
}
@@ -2505,10 +2629,12 @@ 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;
- /* Make sure the connection is still valid if we got here by fetching a photo url */
- /* TODO: Instead of this, we should be canceling this when we disconnect */
- if (url_text && (error_message != NULL ||
- g_list_find(purple_connections_get_all(), info_data->gc) == NULL))
+ 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)
{
purple_debug_warning("msn", "invalid connection. ignoring buddy photo info.\n");
g_free(stripped);
@@ -2563,8 +2689,10 @@ msn_got_photo(PurpleUtilFetchUrlData *url_data, gpointer user_data,
static void
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;
@@ -2572,9 +2700,10 @@ msn_get_info(PurpleConnection *gc, const char *name)
url = g_strdup_printf("%s%s", PROFILE_URL, name);
- purple_util_fetch_url(url, FALSE,
- "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)",
- TRUE, msn_got_info, data);
+ 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);
}
@@ -2665,75 +2794,77 @@ static gboolean msn_uri_handler(const char *proto, const char *cmd, GHashTable *
static PurplePluginProtocolInfo prpl_info =
{
OPT_PROTO_MAIL_CHECK,
- NULL, /* user_splits */
- NULL, /* protocol_options */
- {"png,gif", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_SEND}, /* icon_spec */
- msn_list_icon, /* list_icon */
- msn_list_emblems, /* list_emblems */
- msn_status_text, /* status_text */
- msn_tooltip_text, /* tooltip_text */
- msn_status_types, /* away_states */
- msn_blist_node_menu, /* blist_node_menu */
- NULL, /* chat_info */
- NULL, /* chat_info_defaults */
- msn_login, /* login */
- msn_close, /* close */
- msn_send_im, /* send_im */
- NULL, /* set_info */
- msn_send_typing, /* send_typing */
- msn_get_info, /* get_info */
- msn_set_status, /* set_away */
- msn_set_idle, /* set_idle */
- NULL, /* change_passwd */
- msn_add_buddy, /* add_buddy */
- NULL, /* add_buddies */
- msn_rem_buddy, /* remove_buddy */
- NULL, /* remove_buddies */
- msn_add_permit, /* add_permit */
- msn_add_deny, /* add_deny */
- msn_rem_permit, /* rem_permit */
- msn_rem_deny, /* rem_deny */
- msn_set_permit_deny, /* set_permit_deny */
- NULL, /* join_chat */
- NULL, /* reject chat invite */
- 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 */
- NULL, /* buddy_free */
- msn_convo_closed, /* convo_closed */
- msn_normalize, /* normalize */
- msn_set_buddy_icon, /* set_buddy_icon */
- msn_remove_group, /* 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 */
- msn_can_receive_file, /* can_receive_file */
- msn_send_file, /* send_file */
- msn_new_xfer, /* new_xfer */
- msn_offline_message, /* offline_message */
- NULL, /* whiteboard_prpl_ops */
- NULL, /* send_raw */
- NULL, /* roomlist_room_serialize */
- 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 */
+ NULL, /* user_splits */
+ NULL, /* protocol_options */
+ {"png,gif", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_SEND}, /* icon_spec */
+ msn_list_icon, /* list_icon */
+ msn_list_emblems, /* list_emblems */
+ msn_status_text, /* status_text */
+ msn_tooltip_text, /* tooltip_text */
+ msn_status_types, /* away_states */
+ msn_blist_node_menu, /* blist_node_menu */
+ NULL, /* chat_info */
+ NULL, /* chat_info_defaults */
+ msn_login, /* login */
+ msn_close, /* close */
+ msn_send_im, /* send_im */
+ NULL, /* set_info */
+ msn_send_typing, /* send_typing */
+ msn_get_info, /* get_info */
+ msn_set_status, /* set_away */
+ msn_set_idle, /* set_idle */
+ NULL, /* change_passwd */
+ msn_add_buddy, /* add_buddy */
+ NULL, /* add_buddies */
+ msn_rem_buddy, /* remove_buddy */
+ NULL, /* remove_buddies */
+ msn_add_permit, /* add_permit */
+ msn_add_deny, /* add_deny */
+ msn_rem_permit, /* rem_permit */
+ msn_rem_deny, /* rem_deny */
+ msn_set_permit_deny, /* set_permit_deny */
+ NULL, /* join_chat */
+ NULL, /* reject chat invite */
+ 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 */
+ NULL, /* buddy_free */
+ msn_convo_closed, /* convo_closed */
+ msn_normalize, /* normalize */
+ msn_set_buddy_icon, /* set_buddy_icon */
+ msn_remove_group, /* 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 */
+ msn_can_receive_file, /* can_receive_file */
+ msn_send_file, /* send_file */
+ msn_new_xfer, /* new_xfer */
+ msn_offline_message, /* offline_message */
+ NULL, /* whiteboard_prpl_ops */
+ NULL, /* send_raw */
+ NULL, /* roomlist_room_serialize */
+ 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 */
};
static PurplePluginInfo info =
@@ -2800,6 +2931,11 @@ init_plugin(PurplePlugin *plugin)
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
option);
+ option = purple_account_option_bool_new(_("Allow direct connections"),
+ "direct_connect", TRUE);
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
+ option);
+
purple_cmd_register("nudge", "", PURPLE_CMD_P_PRPL,
PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY,
"prpl-msn", msn_cmd_nudge,
diff --git a/libpurple/protocols/msn/msn.h b/libpurple/protocols/msn/msn.h
index d989ff33eb..1e597cf13d 100644
--- a/libpurple/protocols/msn/msn.h
+++ b/libpurple/protocols/msn/msn.h
@@ -144,7 +144,10 @@ typedef enum
#define MSN_CLIENT_EXT_ID 0
gboolean msn_email_is_valid(const char *passport);
-void msn_act_id(PurpleConnection *gc, const char *entry);
+void
+msn_set_public_alias(PurpleConnection *gc, const char *alias,
+ PurpleSetPublicAliasSuccessCallback success_cb,
+ PurpleSetPublicAliasFailureCallback failure_cb);
void msn_send_privacy(PurpleConnection *gc);
void msn_send_im_message(MsnSession *session, MsnMessage *msg);
diff --git a/libpurple/protocols/msn/msnutils.c b/libpurple/protocols/msn/msnutils.c
index c8870d5603..f86cf3c647 100644
--- a/libpurple/protocols/msn/msnutils.c
+++ b/libpurple/protocols/msn/msnutils.c
@@ -541,7 +541,7 @@ msn_handle_chl(char *input, char *output)
chlStringParts = (unsigned int *)buf;
/* this is magic */
- for (i = 0; i < (strlen(buf) / 4); i += 2) {
+ for (i = 0; i < (len / 4); i += 2) {
long long temp;
chlStringParts[i] = GUINT_TO_LE(chlStringParts[i]);
diff --git a/libpurple/protocols/msn/notification.c b/libpurple/protocols/msn/notification.c
index f8a662ca6b..2f4e9ce054 100644
--- a/libpurple/protocols/msn/notification.c
+++ b/libpurple/protocols/msn/notification.c
@@ -92,7 +92,6 @@ connect_cb(MsnServConn *servconn)
{
MsnCmdProc *cmdproc;
MsnSession *session;
- PurpleAccount *account;
GString *vers;
const char *ver_str;
int i;
@@ -101,7 +100,6 @@ connect_cb(MsnServConn *servconn)
cmdproc = servconn->cmdproc;
session = servconn->session;
- account = session->account;
vers = g_string_new("");
@@ -178,10 +176,8 @@ static void
usr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
MsnSession *session;
- PurpleAccount *account;
session = cmdproc->session;
- account = session->account;
if (!g_ascii_strcasecmp(cmd->params[1], "OK"))
{
@@ -409,7 +405,7 @@ typedef struct MsnFqyCbData {
/* add contact to xmlnode */
static void
-msn_add_contact_xml(MsnSession *session, xmlnode *mlNode,const char *passport, MsnListOp list_op, MsnNetwork networkId)
+msn_add_contact_xml(xmlnode *mlNode, const char *passport, MsnListOp list_op, MsnNetwork networkId)
{
xmlnode *d_node,*c_node;
char **tokens;
@@ -543,8 +539,8 @@ update_contact_network(MsnSession *session, const char *passport, MsnNetwork net
adl_node = xmlnode_new("ml");
xmlnode_set_attrib(adl_node, "l", "1");
- msn_add_contact_xml(session, adl_node, passport,
- user->list_op & MSN_LIST_OP_MASK, network);
+ msn_add_contact_xml(adl_node, passport,
+ user->list_op & MSN_LIST_OP_MASK, network);
payload = xmlnode_to_str(adl_node, &payload_len);
msn_notification_post_adl(session->notification->cmdproc, payload, payload_len);
g_free(payload);
@@ -567,6 +563,7 @@ msn_notification_dump_contact(MsnSession *session)
int payload_len;
int adl_count = 0;
int fqy_count = 0;
+ PurpleConnection *pc;
const char *display_name;
adl_node = xmlnode_new("ml");
@@ -599,8 +596,9 @@ msn_notification_dump_contact(MsnSession *session)
}
if (user->networkid != MSN_NETWORK_UNKNOWN) {
- msn_add_contact_xml(session, adl_node, user->passport,
- user->list_op & MSN_LIST_OP_MASK, user->networkid);
+ msn_add_contact_xml(adl_node, user->passport,
+ user->list_op & MSN_LIST_OP_MASK,
+ user->networkid);
/* each ADL command may contain up to 150 contacts */
if (++adl_count % 150 == 0) {
@@ -629,8 +627,7 @@ msn_notification_dump_contact(MsnSession *session)
purple_debug_info("msn", "Adding FQY address, count is %d\n",
session->adl_fqy);
- msn_add_contact_xml(session, fqy_node, user->passport,
- 0, user->networkid);
+ msn_add_contact_xml(fqy_node, user->passport, 0, user->networkid);
/* each FQY command may contain up to 150 contacts, probably */
if (++fqy_count % 150 == 0) {
@@ -673,11 +670,14 @@ msn_notification_dump_contact(MsnSession *session)
xmlnode_free(adl_node);
xmlnode_free(fqy_node);
- display_name = purple_connection_get_display_name(session->account->gc);
+ msn_session_activate_login_timeout(session);
+
+ pc = purple_account_get_connection(session->account);
+ display_name = purple_connection_get_display_name(pc);
if (display_name
&& strcmp(display_name,
purple_account_get_username(session->account))) {
- msn_act_id(session->account->gc, display_name);
+ msn_set_public_alias(pc, display_name, NULL, NULL);
}
}
@@ -983,19 +983,12 @@ qng_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
static void
fln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
- MsnSlpLink *slplink;
MsnUser *user;
/* Tell libpurple that the user has signed off */
user = msn_userlist_find_user(cmdproc->session->userlist, cmd->params[0]);
msn_user_set_state(user, NULL);
msn_user_update(user);
-
- /* If we have an open MsnSlpLink with the user then close it */
- slplink = msn_session_find_slplink(cmdproc->session, cmd->params[0]);
- if (slplink != NULL)
- msn_slplink_destroy(slplink);
-
}
static void
@@ -1003,7 +996,6 @@ iln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
MsnSession *session;
PurpleAccount *account;
- PurpleConnection *gc;
MsnUser *user;
MsnObject *msnobj = NULL;
unsigned long clientid;
@@ -1013,7 +1005,6 @@ iln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
session = cmdproc->session;
account = session->account;
- gc = purple_account_get_connection(account);
state = cmd->params[1];
passport = cmd->params[2];
@@ -1206,7 +1197,6 @@ nln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
MsnSession *session;
PurpleAccount *account;
- PurpleConnection *gc;
MsnUser *user;
MsnObject *msnobj;
unsigned long clientid;
@@ -1215,7 +1205,6 @@ nln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
session = cmdproc->session;
account = session->account;
- gc = purple_account_get_connection(account);
state = cmd->params[0];
passport = cmd->params[1];
@@ -1299,11 +1288,11 @@ static void
prp_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
MsnSession *session = cmdproc->session;
- const char *type, *value, *friendlyname;
+ const char *type, *value;
g_return_if_fail(cmd->param_count >= 3);
- type = cmd->params[2];
+ type = cmd->params[2];
if (cmd->param_count == 4)
{
@@ -1323,19 +1312,6 @@ prp_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
msn_user_set_work_phone(session->user, NULL);
else if (!strcmp(type, "PHM"))
msn_user_set_mobile_phone(session->user, NULL);
- else {
- type = cmd->params[1];
- if (!strcmp(type, "MFN")) {
- friendlyname = purple_url_decode(cmd->params[2]);
-
- msn_update_contact(session, "Me", MSN_UPDATE_DISPLAY, friendlyname);
-
- purple_connection_set_display_name(
- purple_account_get_connection(session->account),
- friendlyname);
- purple_account_set_string(session->account, "display-name", friendlyname);
- }
- }
}
}
@@ -1403,11 +1379,13 @@ rng_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
MsnSession *session;
MsnSwitchBoard *swboard;
const char *session_id;
+ const char *auth_key;
char *host;
int port;
session = cmdproc->session;
session_id = cmd->params[0];
+ auth_key = cmd->params[3];
msn_parse_socket(cmd->params[1], &host, &port);
@@ -1417,8 +1395,8 @@ rng_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
swboard = msn_switchboard_new(session);
msn_switchboard_set_invited(swboard, TRUE);
- msn_switchboard_set_session_id(swboard, cmd->params[0]);
- msn_switchboard_set_auth_key(swboard, cmd->params[3]);
+ msn_switchboard_set_session_id(swboard, session_id);
+ msn_switchboard_set_auth_key(swboard, auth_key);
swboard->im_user = g_strdup(cmd->params[4]);
/* msn_switchboard_add_user(swboard, cmd->params[4]); */
@@ -1587,18 +1565,16 @@ ubx_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
size_t len)
{
MsnSession *session;
- PurpleAccount *account;
MsnUser *user;
const char *passport;
- char *psm_str, *str;
+ char *str;
session = cmdproc->session;
- account = session->account;
passport = cmd->params[0];
user = msn_userlist_find_user(session->userlist, passport);
if (user == NULL) {
- char *str = g_strndup(payload, len);
+ str = g_strndup(payload, len);
purple_debug_info("msn", "unknown user %s, payload is %s\n",
passport, str);
g_free(str);
@@ -1613,12 +1589,13 @@ ubx_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
user->extinfo->media_album = NULL;
user->extinfo->media_artist = NULL;
user->extinfo->media_title = NULL;
+ user->extinfo->media_type = CURRENT_MEDIA_UNKNOWN;
}
if (len != 0) {
- psm_str = msn_get_psm(cmd->payload,len);
- msn_user_set_statusline(user, psm_str);
- g_free(psm_str);
+ str = msn_get_psm(cmd->payload,len);
+ msn_user_set_statusline(user, str);
+ g_free(str);
str = msn_get_currentmedia(cmd->payload, len);
parse_currentmedia(user, str);
@@ -1664,7 +1641,9 @@ profile_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
{
MsnSession *session;
const char *value;
+#ifdef MSN_PARTIAL_LISTS
const char *clLastChange;
+#endif
session = cmdproc->session;
@@ -1707,9 +1686,9 @@ profile_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
if ((value = msn_message_get_attr(msg, "EmailEnabled")) != NULL)
session->passport_info.email_enabled = (gboolean)atol(value);
+#ifdef MSN_PARTIAL_LISTS
/*starting retrieve the contact list*/
clLastChange = purple_account_get_string(session->account, "CLLastChange", NULL);
-#ifdef MSN_PARTIAL_LISTS
/* msn_userlist_load defeats all attempts at trying to detect blist sync issues */
msn_userlist_load(session);
msn_get_contact_list(session, MSN_PS_INITIAL, clLastChange);
@@ -1913,7 +1892,7 @@ system_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
if ((type_s = g_hash_table_lookup(table, "Type")) != NULL)
{
int type = atoi(type_s);
- char buf[MSN_BUF_LEN];
+ char buf[MSN_BUF_LEN] = "";
int minutes;
switch (type)
@@ -1973,8 +1952,7 @@ modify_unknown_buddy_on_list(MsnSession *session, const char *passport,
node = xmlnode_new("ml");
node->child = NULL;
- msn_add_contact_xml(session, node, passport,
- addrem->list_op, network);
+ msn_add_contact_xml(node, passport, addrem->list_op, network);
payload = xmlnode_to_str(node, &payload_len);
xmlnode_free(node);
@@ -2004,8 +1982,7 @@ msn_notification_add_buddy_to_list(MsnNotification *notification, MsnListId list
adl_node = xmlnode_new("ml");
adl_node->child = NULL;
- msn_add_contact_xml(notification->session, adl_node, user->passport,
- list_op, user->networkid);
+ msn_add_contact_xml(adl_node, user->passport, list_op, user->networkid);
payload = xmlnode_to_str(adl_node, &payload_len);
xmlnode_free(adl_node);
@@ -2043,8 +2020,7 @@ msn_notification_rem_buddy_from_list(MsnNotification *notification, MsnListId li
rml_node = xmlnode_new("ml");
rml_node->child = NULL;
- msn_add_contact_xml(notification->session, rml_node, user->passport,
- list_op, user->networkid);
+ msn_add_contact_xml(rml_node, user->passport, list_op, user->networkid);
payload = xmlnode_to_str(rml_node, &payload_len);
xmlnode_free(rml_node);
diff --git a/libpurple/protocols/msn/notification.h b/libpurple/protocols/msn/notification.h
index 96991e4952..776f10ad20 100644
--- a/libpurple/protocols/msn/notification.h
+++ b/libpurple/protocols/msn/notification.h
@@ -28,6 +28,10 @@ typedef struct _MsnNotification MsnNotification;
/* MSN protocol challenge info */
+/* MSNP18 challenge: WLM Version 2009 (Build 14.0.8089.726) */
+#define MSNP18_WLM_PRODUCT_KEY "C1BX{V4W}Q3*10SM"
+#define MSNP18_WLM_PRODUCT_ID "PROD0120PW!CCV9@"
+
/* MSNP15 challenge: WLM 8.5.1288.816 */
#define MSNP15_WLM_PRODUCT_KEY "ILTXC!4IXB5FB*PX"
#define MSNP15_WLM_PRODUCT_ID "PROD0119GSJUC$18"
diff --git a/libpurple/protocols/msn/object.c b/libpurple/protocols/msn/object.c
index 09b40e26f7..dddbd271ea 100644
--- a/libpurple/protocols/msn/object.c
+++ b/libpurple/protocols/msn/object.c
@@ -96,15 +96,29 @@ msn_object_new_from_string(const char *str)
GET_STRING_TAG(friendly, "Friendly");
GET_STRING_TAG(sha1d, "SHA1D");
GET_STRING_TAG(sha1c, "SHA1C");
+ GET_STRING_TAG(url, "Url");
+ GET_STRING_TAG(url1, "Url1");
/* If we are missing any of the required elements then discard the object */
- /* SHA1C is not always sent anymore */
if (obj->creator == NULL || obj->size == 0 || obj->type == 0
- || obj->location == NULL || obj->friendly == NULL
- || obj->sha1d == NULL /*|| obj->sha1c == NULL*/) {
+ || obj->sha1d == NULL) {
purple_debug_error("msn", "Discarding invalid msnobj: '%s'\n", str);
msn_object_destroy(obj);
- obj = NULL;
+ return NULL;
+ }
+
+ if (obj->location == NULL || obj->friendly == NULL) {
+ /* 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);
+ 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);
+ return NULL;
+ }
}
return obj;
@@ -284,6 +298,24 @@ msn_object_set_sha1c(MsnObject *obj, const char *sha1c)
obj->sha1c = g_strdup(sha1c);
}
+void
+msn_object_set_url(MsnObject *obj, const char *url)
+{
+ g_return_if_fail(obj != NULL);
+
+ g_free(obj->url);
+ obj->url = g_strdup(url);
+}
+
+void
+msn_object_set_url1(MsnObject *obj, const char *url)
+{
+ g_return_if_fail(obj != NULL);
+
+ g_free(obj->url1);
+ obj->url1 = g_strdup(url);
+}
+
const char *
msn_object_get_creator(const MsnObject *obj)
{
@@ -352,6 +384,22 @@ msn_object_get_sha1(const MsnObject *obj)
}
}
+const char *
+msn_object_get_url(const MsnObject *obj)
+{
+ g_return_val_if_fail(obj != NULL, NULL);
+
+ return obj->url;
+}
+
+const char *
+msn_object_get_url1(const MsnObject *obj)
+{
+ g_return_val_if_fail(obj != NULL, NULL);
+
+ return obj->url1;
+}
+
static MsnObject *
msn_object_find_local(const char *sha1)
{
diff --git a/libpurple/protocols/msn/object.h b/libpurple/protocols/msn/object.h
index 7bcab67514..d2745118f6 100644
--- a/libpurple/protocols/msn/object.h
+++ b/libpurple/protocols/msn/object.h
@@ -50,6 +50,8 @@ typedef struct
char *friendly;
char *sha1d;
char *sha1c;
+ char *url;
+ char *url1;
} MsnObject;
/**
@@ -155,6 +157,20 @@ void msn_object_set_sha1c(MsnObject *obj, const char *sha1c);
void msn_object_set_image(MsnObject *obj, PurpleStoredImage *img);
/**
+ * Sets the url field in a MsnObject.
+ *
+ * @param url The url value.
+ */
+void msn_object_set_url(MsnObject *obj, const char *url);
+
+/**
+ * Sets the url1 field in a MsnObject.
+ *
+ * @param url1 The url1 value.
+ */
+void msn_object_set_url1(MsnObject *obj, const char *url);
+
+/**
* Returns a MsnObject's creator value.
*
* @param obj The object.
@@ -235,6 +251,24 @@ const char *msn_object_get_sha1(const MsnObject *obj);
*/
PurpleStoredImage *msn_object_get_image(const MsnObject *obj);
+/**
+ * Returns a MsnObject's url value.
+ *
+ * @param obj The object.
+ *
+ * @return The url value.
+ */
+const char *msn_object_get_url(const MsnObject *obj);
+
+/**
+ * Returns a MsnObject's url1 value.
+ *
+ * @param obj The object.
+ *
+ * @return The url1 value.
+ */
+const char *msn_object_get_url1(const MsnObject *obj);
+
void msn_object_set_local(MsnObject *obj);
#endif /* MSN_OBJECT_H */
diff --git a/libpurple/protocols/msn/session.c b/libpurple/protocols/msn/session.c
index 1517717fdd..0b763caa1c 100644
--- a/libpurple/protocols/msn/session.c
+++ b/libpurple/protocols/msn/session.c
@@ -57,6 +57,11 @@ 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);
+ }
+
if (session->connected)
msn_session_disconnect(session);
@@ -131,6 +136,11 @@ msn_session_disconnect(MsnSession *session)
if (!session->connected)
return;
+ if (session->login_timeout) {
+ purple_timeout_remove(session->login_timeout);
+ session->login_timeout = 0;
+ }
+
session->connected = FALSE;
while (session->switches != NULL)
@@ -258,6 +268,28 @@ msn_session_get_swboard(MsnSession *session, const char *username,
return swboard;
}
+static gboolean
+msn_login_timeout_cb(gpointer data)
+{
+ MsnSession *session = data;
+ /* This forces the login process to finish, even though we haven't heard
+ a response for our FQY requests yet. We'll at least end up online to the
+ people we've already added. The rest will follow later. */
+ msn_session_finish_login(session);
+ session->login_timeout = 0;
+ return FALSE;
+}
+
+void
+msn_session_activate_login_timeout(MsnSession *session)
+{
+ if (!session->logged_in && session->connected) {
+ session->login_timeout =
+ purple_timeout_add_seconds(MSN_LOGIN_FQY_TIMEOUT,
+ msn_login_timeout_cb, session);
+ }
+}
+
static void
msn_session_sync_users(MsnSession *session)
{
@@ -362,6 +394,9 @@ msn_session_set_error(MsnSession *session, MsnErrorType error,
msg = g_strdup_printf(_("Unable to authenticate: %s"),
(info == NULL ) ?
_("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);
break;
case MSN_ERROR_BAD_BLIST:
reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
@@ -407,7 +442,7 @@ msn_session_set_login_step(MsnSession *session, MsnLoginStep step)
/* Prevent the connection progress going backwards, eg. if we get
* transferred several times during login */
- if (session->login_step > step)
+ if (session->login_step >= step)
return;
/* If we're already logged in, we're probably here because of a
diff --git a/libpurple/protocols/msn/session.h b/libpurple/protocols/msn/session.h
index dec6353f47..27ad890b16 100644
--- a/libpurple/protocols/msn/session.h
+++ b/libpurple/protocols/msn/session.h
@@ -59,6 +59,8 @@ typedef enum
#define MSN_LOGIN_STEPS MSN_LOGIN_STEP_END
+#define MSN_LOGIN_FQY_TIMEOUT 30
+
#include "group.h"
#include "httpconn.h"
#include "nexus.h"
@@ -83,6 +85,7 @@ struct _MsnSession
gboolean connected;
gboolean logged_in; /**< A temporal flag to ignore local buddy list adds. */
int adl_fqy; /**< A count of ADL/FQY so status is only changed once. */
+ guint login_timeout; /**< Timeout to force status change if ADL/FQY fail. */
gboolean destroying; /**< A flag that states if the session is being destroyed. */
gboolean http_method;
@@ -119,6 +122,8 @@ struct _MsnSession
GHashTable *soap_table;
guint soap_cleanup_handle;
+
+ GSList *url_datas; /**< PurpleUtilFetchUrlData to be cancelled on exit */
};
/**
@@ -213,7 +218,16 @@ void msn_session_set_error(MsnSession *session, MsnErrorType error,
const char *info);
/**
- * Sets the current step in the login proccess.
+ * Starts a timeout to initiate finishing login. Sometimes the server ignores
+ * our FQY requests, so this forces ourselves online eventually.
+ *
+ * @param session The MSN session.
+ */
+void
+msn_session_activate_login_timeout(MsnSession *session);
+
+/**
+ * Sets the current step in the login process.
*
* @param session The MSN session.
* @param step The current step.
diff --git a/libpurple/protocols/msn/slp.c b/libpurple/protocols/msn/slp.c
index b55e48b8e1..e7f7dc58f2 100644
--- a/libpurple/protocols/msn/slp.c
+++ b/libpurple/protocols/msn/slp.c
@@ -25,24 +25,26 @@
#include "slp.h"
#include "slpcall.h"
#include "slpmsg.h"
+#include "msnutils.h"
#include "object.h"
#include "user.h"
#include "switchboard.h"
+#include "directconn.h"
#include "smiley.h"
-/* ms to delay between sending buddy icon requests to the server. */
+/* seconds to delay between sending buddy icon requests to the server. */
#define BUDDY_ICON_DELAY 20
-static void send_ok(MsnSlpCall *slpcall, const char *branch,
- const char *type, const char *content);
-
-static void send_decline(MsnSlpCall *slpcall, const char *branch,
- const char *type, const char *content);
-
static void request_user_display(MsnUser *user);
+typedef struct {
+ MsnSession *session;
+ const char *remote_user;
+ const char *sha1;
+} MsnFetchUserDisplayData;
+
/**************************************************************************
* Util
**************************************************************************/
@@ -91,7 +93,7 @@ msn_xfer_init(PurpleXfer *xfer)
content = g_strdup_printf("SessionID: %lu\r\n\r\n",
slpcall->session_id);
- send_ok(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody",
+ msn_slp_send_ok(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody",
content);
g_free(content);
@@ -120,7 +122,7 @@ msn_xfer_cancel(PurpleXfer *xfer)
content = g_strdup_printf("SessionID: %lu\r\n\r\n",
slpcall->session_id);
- send_decline(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody",
+ msn_slp_send_decline(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody",
content);
g_free(content);
@@ -154,6 +156,7 @@ msn_xfer_write(const guchar *data, gsize len, PurpleXfer *xfer)
slpcall->u.outgoing.len = len;
slpcall->u.outgoing.data = data;
msn_slplink_send_msgpart(slpcall->slplink, slpcall->xfer_msg);
+ msn_message_unref(slpcall->xfer_msg->msg);
return MIN(1202, len);
}
@@ -198,6 +201,7 @@ msn_xfer_completed_cb(MsnSlpCall *slpcall, const guchar *body,
gsize size)
{
PurpleXfer *xfer = slpcall->xfer;
+
purple_xfer_set_completed(xfer, TRUE);
purple_xfer_end(xfer);
}
@@ -206,36 +210,8 @@ msn_xfer_completed_cb(MsnSlpCall *slpcall, const guchar *body,
* SLP Control
**************************************************************************/
-#if 0
-static void
-got_transresp(MsnSlpCall *slpcall, const char *nonce,
- const char *ips_str, int port)
-{
- MsnDirectConn *directconn;
- char **ip_addrs, **c;
-
- directconn = msn_directconn_new(slpcall->slplink);
-
- directconn->initial_call = slpcall;
-
- /* msn_directconn_parse_nonce(directconn, nonce); */
- directconn->nonce = g_strdup(nonce);
-
- ip_addrs = g_strsplit(ips_str, " ", -1);
-
- for (c = ip_addrs; *c != NULL; c++)
- {
- purple_debug_info("msn", "ip_addr = %s\n", *c);
- if (msn_directconn_connect(directconn, *c, port))
- break;
- }
-
- g_strfreev(ip_addrs);
-}
-#endif
-
-static void
-send_ok(MsnSlpCall *slpcall, const char *branch,
+void
+msn_slp_send_ok(MsnSlpCall *slpcall, const char *branch,
const char *type, const char *content)
{
MsnSlpLink *slplink;
@@ -252,12 +228,10 @@ send_ok(MsnSlpCall *slpcall, const char *branch,
slpmsg->text_body = TRUE;
msn_slplink_queue_slpmsg(slplink, slpmsg);
-
- msn_slpcall_session_init(slpcall);
}
-static void
-send_decline(MsnSlpCall *slpcall, const char *branch,
+void
+msn_slp_send_decline(MsnSlpCall *slpcall, const char *branch,
const char *type, const char *content)
{
MsnSlpLink *slplink;
@@ -308,6 +282,190 @@ find_valid_emoticon(PurpleAccount *account, const char *path)
return NULL;
}
+static char *
+parse_dc_nonce(const char *content, MsnDirectConnNonceType *ntype)
+{
+ char *nonce;
+
+ *ntype = DC_NONCE_UNKNOWN;
+
+ nonce = get_token(content, "Hashed-Nonce: {", "}\r\n");
+ if (nonce) {
+ *ntype = DC_NONCE_SHA1;
+ } else {
+ guint32 n1, n6;
+ guint16 n2, n3, n4, n5;
+ nonce = get_token(content, "Nonce: {", "}\r\n");
+ if (nonce
+ && sscanf(nonce, "%08x-%04hx-%04hx-%04hx-%04hx%08x",
+ &n1, &n2, &n3, &n4, &n5, &n6) == 6) {
+ *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);
+ } else {
+ /* Invalid nonce, so ignore request */
+ g_free(nonce);
+ nonce = NULL;
+ }
+ }
+
+ return nonce;
+}
+
+static void
+msn_slp_process_transresp(MsnSlpCall *slpcall, const char *content)
+{
+ /* A direct connection negotiation response */
+ char *bridge;
+ char *nonce;
+ char *listening;
+ MsnDirectConn *dc = slpcall->slplink->dc;
+ MsnDirectConnNonceType ntype;
+
+ purple_debug_info("msn", "process_transresp\n");
+
+ /* Direct connections are disabled. */
+ if (!purple_account_get_bool(slpcall->slplink->session->account, "direct_connect", TRUE))
+ return;
+
+ g_return_if_fail(dc != NULL);
+ g_return_if_fail(dc->state == DC_STATE_CLOSED);
+
+ bridge = get_token(content, "Bridge: ", "\r\n");
+ nonce = parse_dc_nonce(content, &ntype);
+ listening = get_token(content, "Listening: ", "\r\n");
+ if (listening && bridge && !strcmp(bridge, "TCPv1")) {
+ /* Ok, the client supports direct TCP connection */
+
+ /* We always need this. */
+ if (ntype == DC_NONCE_SHA1) {
+ strncpy(dc->remote_nonce, nonce, 36);
+ dc->remote_nonce[36] = '\0';
+ }
+
+ if (!strcasecmp(listening, "false")) {
+ if (dc->listen_data != NULL) {
+ /*
+ * We'll listen for incoming connections but
+ * the listening socket isn't ready yet so we cannot
+ * send the INVITE packet now. Put the slpcall into waiting mode
+ * and let the callback send the invite.
+ */
+ slpcall->wait_for_socket = TRUE;
+
+ } else if (dc->listenfd != -1) {
+ /* The listening socket is ready. Send the INVITE here. */
+ msn_dc_send_invite(dc);
+
+ } else {
+ /* We weren't able to create a listener either. Use SB. */
+ msn_dc_fallback_to_sb(dc);
+ }
+
+ } else {
+ /*
+ * We should connect to the client so parse
+ * IP/port from response.
+ */
+ char *ip, *port_str;
+ int port = 0;
+
+ if (ntype == DC_NONCE_PLAIN) {
+ /* Only needed for listening side. */
+ memcpy(dc->nonce, nonce, 16);
+ }
+
+ /* Cancel any listen attempts because we don't need them. */
+ if (dc->listenfd_handle != 0) {
+ purple_input_remove(dc->listenfd_handle);
+ dc->listenfd_handle = 0;
+ }
+ if (dc->connect_timeout_handle != 0) {
+ purple_timeout_remove(dc->connect_timeout_handle);
+ dc->connect_timeout_handle = 0;
+ }
+ if (dc->listenfd != -1) {
+ purple_network_remove_port_mapping(dc->listenfd);
+ close(dc->listenfd);
+ dc->listenfd = -1;
+ }
+ if (dc->listen_data != NULL) {
+ purple_network_listen_cancel(dc->listen_data);
+ dc->listen_data = NULL;
+ }
+
+ /* Save external IP/port for later use. We'll try local connection first. */
+ dc->ext_ip = get_token(content, "IPv4External-Addrs: ", "\r\n");
+ port_str = get_token(content, "IPv4External-Port: ", "\r\n");
+ if (port_str) {
+ dc->ext_port = atoi(port_str);
+ g_free(port_str);
+ }
+
+ ip = get_token(content, "IPv4Internal-Addrs: ", "\r\n");
+ port_str = get_token(content, "IPv4Internal-Port: ", "\r\n");
+ if (port_str) {
+ port = atoi(port_str);
+ g_free(port_str);
+ }
+
+ if (ip && port) {
+ /* Try internal address first */
+ dc->connect_data = purple_proxy_connect(
+ NULL,
+ slpcall->slplink->session->account,
+ ip,
+ port,
+ msn_dc_connected_to_peer_cb,
+ dc
+ );
+
+ if (dc->connect_data) {
+ /* Add connect timeout handle */
+ dc->connect_timeout_handle = purple_timeout_add_seconds(
+ DC_OUTGOING_TIMEOUT,
+ msn_dc_outgoing_connection_timeout_cb,
+ dc
+ );
+ } else {
+ /*
+ * Connection failed
+ * Try external IP/port (if specified)
+ */
+ msn_dc_outgoing_connection_timeout_cb(dc);
+ }
+
+ } else {
+ /*
+ * Omitted or invalid internal IP address / port
+ * Try external IP/port (if specified)
+ */
+ msn_dc_outgoing_connection_timeout_cb(dc);
+ }
+
+ g_free(ip);
+ }
+
+ } else {
+ /*
+ * Invalid direct connect invitation or
+ * TCP connection is not supported
+ */
+ }
+
+ g_free(listening);
+ g_free(nonce);
+ g_free(bridge);
+
+ return;
+}
+
static void
got_sessionreq(MsnSlpCall *slpcall, const char *branch,
const char *euf_guid, const char *context)
@@ -330,7 +488,7 @@ got_sessionreq(MsnSlpCall *slpcall, const char *branch,
content = g_strdup_printf("SessionID: %lu\r\n\r\n",
slpcall->session_id);
- send_ok(slpcall, branch, "application/x-msnmsgr-sessionreqbody",
+ msn_slp_send_ok(slpcall, branch, "application/x-msnmsgr-sessionreqbody",
content);
g_free(content);
@@ -422,6 +580,12 @@ got_sessionreq(MsnSlpCall *slpcall, const char *branch,
xfer->data = slpcall;
+ if (header->type == 0 && bin_len >= sizeof(MsnFileContext)) {
+ purple_xfer_set_thumbnail(xfer, &header->preview,
+ bin_len - sizeof(MsnFileContext),
+ "image/png");
+ }
+
purple_xfer_request(xfer);
}
g_free(header);
@@ -479,7 +643,7 @@ got_sessionreq(MsnSlpCall *slpcall, const char *branch,
if (!accepted) {
char *content = g_strdup_printf("SessionID: %lu\r\n\r\n",
slpcall->session_id);
- send_decline(slpcall, branch, "application/x-msnmsgr-sessionreqbody", content);
+ msn_slp_send_decline(slpcall, branch, "application/x-msnmsgr-sessionreqbody", content);
g_free(content);
}
}
@@ -548,92 +712,105 @@ got_invite(MsnSlpCall *slpcall,
}
else if (!strcmp(type, "application/x-msnmsgr-transreqbody"))
{
- /* A direct connection? */
+ /* A direct connection negotiation request */
+ char *bridges;
+ char *nonce;
+ MsnDirectConnNonceType ntype;
- char *listening, *nonce;
- char *content;
+ purple_debug_info("msn", "got_invite: transreqbody received\n");
- if (FALSE)
- {
-#if 0
- MsnDirectConn *directconn;
- /* const char *ip_addr; */
- char *ip_port;
- int port;
+ /* Direct connections may be disabled. */
+ if (!purple_account_get_bool(slplink->session->account, "direct_connect", TRUE)) {
+ msn_slp_send_ok(slpcall, branch,
+ "application/x-msnmsgr-transrespbody",
+ "Bridge: TCPv1\r\n"
+ "Listening: false\r\n"
+ "Nonce: {00000000-0000-0000-0000-000000000000}\r\n"
+ "\r\n");
+ msn_slpcall_session_init(slpcall);
- /* ip_addr = purple_prefs_get_string("/purple/ft/public_ip"); */
- ip_port = "5190";
- listening = "true";
- nonce = rand_guid();
+ return;
+ }
- directconn = msn_directconn_new(slplink);
+ /* Don't do anything if we already have a direct connection */
+ if (slplink->dc != NULL)
+ return;
- /* msn_directconn_parse_nonce(directconn, nonce); */
- directconn->nonce = g_strdup(nonce);
+ bridges = get_token(content, "Bridges: ", "\r\n");
+ nonce = parse_dc_nonce(content, &ntype);
+ if (bridges && strstr(bridges, "TCPv1") != NULL) {
+ /*
+ * Ok, the client supports direct TCP connection
+ * Try to create a listening port
+ */
+ MsnDirectConn *dc;
+
+ dc = msn_dc_new(slpcall);
+ if (ntype == DC_NONCE_PLAIN) {
+ /* There is only one nonce for plain auth. */
+ dc->nonce_type = ntype;
+ memcpy(dc->nonce, nonce, 16);
+ } else if (ntype == DC_NONCE_SHA1) {
+ /* Each side has a nonce in SHA1 auth. */
+ dc->nonce_type = ntype;
+ strncpy(dc->remote_nonce, nonce, 36);
+ dc->remote_nonce[36] = '\0';
+ }
- msn_directconn_listen(directconn);
+ dc->listen_data = purple_network_listen_range(
+ 0, 0,
+ SOCK_STREAM,
+ msn_dc_listen_socket_created_cb,
+ dc
+ );
- port = directconn->port;
+ if (dc->listen_data == NULL) {
+ /* Listen socket creation failed */
+
+ purple_debug_info("msn", "got_invite: listening failed\n");
+
+ if (dc->nonce_type != DC_NONCE_PLAIN)
+ msn_slp_send_ok(slpcall, branch,
+ "application/x-msnmsgr-transrespbody",
+ "Bridge: TCPv1\r\n"
+ "Listening: false\r\n"
+ "Hashed-Nonce: {00000000-0000-0000-0000-000000000000}\r\n"
+ "\r\n");
+ else
+ msn_slp_send_ok(slpcall, branch,
+ "application/x-msnmsgr-transrespbody",
+ "Bridge: TCPv1\r\n"
+ "Listening: false\r\n"
+ "Nonce: {00000000-0000-0000-0000-000000000000}\r\n"
+ "\r\n");
+
+ } else {
+ /*
+ * Listen socket created successfully.
+ * Don't send anything here because we don't know the parameters
+ * of the created socket yet. msn_dc_send_ok will be called from
+ * the callback function: dc_listen_socket_created_cb
+ */
+ purple_debug_info("msn", "got_invite: listening socket created\n");
- content = g_strdup_printf(
- "Bridge: TCPv1\r\n"
- "Listening: %s\r\n"
- "Nonce: {%s}\r\n"
- "Ipv4Internal-Addrs: 192.168.0.82\r\n"
- "Ipv4Internal-Port: %d\r\n"
- "\r\n",
- listening,
- nonce,
- port);
-#endif
- }
- else
- {
- listening = "false";
- nonce = g_strdup("00000000-0000-0000-0000-000000000000");
+ dc->send_connection_info_msg_cb = msn_dc_send_ok;
+ slpcall->wait_for_socket = TRUE;
+ }
- content = g_strdup_printf(
- "Bridge: TCPv1\r\n"
- "Listening: %s\r\n"
- "Nonce: {%s}\r\n"
- "\r\n",
- listening,
- nonce);
+ } else {
+ /*
+ * Invalid direct connect invitation or
+ * TCP connection is not supported.
+ */
}
- send_ok(slpcall, branch,
- "application/x-msnmsgr-transrespbody", content);
-
- g_free(content);
g_free(nonce);
+ g_free(bridges);
}
else if (!strcmp(type, "application/x-msnmsgr-transrespbody"))
{
-#if 0
- char *ip_addrs;
- char *temp;
- char *nonce;
- int port;
-
- nonce = get_token(content, "Nonce: {", "}\r\n");
- if (ip_addrs == NULL)
- return;
-
- ip_addrs = get_token(content, "IPv4Internal-Addrs: ", "\r\n");
-
- temp = get_token(content, "IPv4Internal-Port: ", "\r\n");
- if (temp != NULL)
- port = atoi(temp);
- else
- port = -1;
- g_free(temp);
-
- if (port > 0)
- got_transresp(slpcall, nonce, ip_addrs, port);
-
- g_free(nonce);
- g_free(ip_addrs);
-#endif
+ /* A direct connection negotiation response */
+ msn_slp_process_transresp(slpcall, content);
}
}
@@ -646,52 +823,104 @@ got_ok(MsnSlpCall *slpcall,
if (!strcmp(type, "application/x-msnmsgr-sessionreqbody"))
{
-#if 0
- if (slpcall->type == MSN_SLPCALL_DC)
- {
- /* First let's try a DirectConnection. */
+ char *content;
+ char *header;
+ char *nonce = NULL;
+ MsnSession *session = slpcall->slplink->session;
+ MsnSlpMessage *msg;
+ MsnDirectConn *dc;
+ MsnUser *user;
+
+ if (!purple_account_get_bool(session->account, "direct_connect", TRUE)) {
+ /* Don't attempt a direct connection if disabled. */
+ msn_slpcall_session_init(slpcall);
+ return;
+ }
- MsnSlpLink *slplink;
- MsnSlpMessage *slpmsg;
- char *header;
- char *content;
- char *branch;
+ if (slpcall->slplink->dc != NULL) {
+ /* If we already have an established direct connection
+ * then just start the transfer.
+ */
+ msn_slpcall_session_init(slpcall);
+ return;
+ }
- slplink = slpcall->slplink;
+ user = msn_userlist_find_user(session->userlist,
+ slpcall->slplink->remote_user);
+ if (!user || !(user->clientid & 0xF0000000)) {
+ /* Just start a normal SB transfer. */
+ msn_slpcall_session_init(slpcall);
+ return;
+ }
+
+ /* Try direct file transfer by sending a second INVITE */
+ dc = msn_dc_new(slpcall);
+ slpcall->branch = rand_guid();
+
+ dc->listen_data = purple_network_listen_range(
+ 0, 0,
+ SOCK_STREAM,
+ msn_dc_listen_socket_created_cb,
+ dc
+ );
+
+ header = g_strdup_printf(
+ "INVITE MSNMSGR:%s MSNSLP/1.0",
+ slpcall->slplink->remote_user
+ );
+
+ if (dc->nonce_type == DC_NONCE_SHA1)
+ nonce = g_strdup_printf("Hashed-Nonce: {%s}\r\n", dc->nonce_hash);
- branch = rand_guid();
+ if (dc->listen_data == NULL) {
+ /* Listen socket creation failed */
+ purple_debug_info("msn", "got_ok: listening failed\n");
content = g_strdup_printf(
- "Bridges: TRUDPv1 TCPv1\r\n"
- "NetID: 0\r\n"
- "Conn-Type: Direct-Connect\r\n"
+ "Bridges: TCPv1\r\n"
+ "NetID: %u\r\n"
+ "Conn-Type: IP-Restrict-NAT\r\n"
"UPnPNat: false\r\n"
"ICF: false\r\n"
+ "%s"
+ "\r\n",
+
+ rand() % G_MAXUINT32,
+ nonce ? nonce : ""
);
- header = g_strdup_printf("INVITE MSNMSGR:%s MSNSLP/1.0",
- slplink->remote_user);
+ } else {
+ /* Listen socket created successfully. */
+ purple_debug_info("msn", "got_ok: listening socket created\n");
- slpmsg = msn_slp_sipmsg_new(slpcall, 0, header, branch,
- "application/x-msnmsgr-transreqbody",
- content);
+ content = g_strdup_printf(
+ "Bridges: TCPv1\r\n"
+ "NetID: 0\r\n"
+ "Conn-Type: Direct-Connect\r\n"
+ "UPnPNat: false\r\n"
+ "ICF: false\r\n"
+ "%s"
+ "\r\n",
- slpmsg->info = "SLP INVITE";
- slpmsg->text_body = TRUE;
- msn_slplink_send_slpmsg(slplink, slpmsg);
+ nonce ? nonce : ""
+ );
+ }
- g_free(header);
- g_free(content);
+ msg = msn_slpmsg_sip_new(
+ slpcall,
+ 0,
+ header,
+ slpcall->branch,
+ "application/x-msnmsgr-transreqbody",
+ content
+ );
+ msg->info = "DC INVITE";
+ msg->text_body = TRUE;
+ g_free(nonce);
+ g_free(header);
+ g_free(content);
- g_free(branch);
- }
- else
- {
- msn_slpcall_session_init(slpcall);
- }
-#else
- msn_slpcall_session_init(slpcall);
-#endif
+ msn_slplink_queue_slpmsg(slpcall->slplink, msg);
}
else if (!strcmp(type, "application/x-msnmsgr-transreqbody"))
{
@@ -700,32 +929,28 @@ got_ok(MsnSlpCall *slpcall,
}
else if (!strcmp(type, "application/x-msnmsgr-transrespbody"))
{
-#if 0
- char *ip_addrs;
- char *temp;
- char *nonce;
- int port;
+ msn_slp_process_transresp(slpcall, content);
+ }
+}
- nonce = get_token(content, "Nonce: {", "}\r\n");
- if (ip_addrs == NULL)
+static void
+got_error(MsnSlpCall *slpcall,
+ const char *error, const char *type, const char *content)
+{
+ /* It's not valid. Kill this off. */
+ purple_debug_error("msn", "Received non-OK result: %s\n",
+ error ? error : "Unknown");
+
+ if (type && (!strcmp(type, "application/x-msnmsgr-transreqbody")
+ || !strcmp(type, "application/x-msnmsgr-transrespbody"))) {
+ MsnDirectConn *dc = slpcall->slplink->dc;
+ if (dc) {
+ msn_dc_fallback_to_sb(dc);
return;
-
- ip_addrs = get_token(content, "IPv4Internal-Addrs: ", "\r\n");
-
- temp = get_token(content, "IPv4Internal-Port: ", "\r\n");
- if (temp != NULL)
- port = atoi(temp);
- else
- port = -1;
- g_free(temp);
-
- if (port > 0)
- got_transresp(slpcall, nonce, ip_addrs, port);
-
- g_free(nonce);
- g_free(ip_addrs);
-#endif
+ }
}
+
+ slpcall->wasted = TRUE;
}
MsnSlpCall *
@@ -767,18 +992,25 @@ msn_slp_sip_recv(MsnSlpLink *slplink, const char *body)
content = get_token(body, "\r\n\r\n", NULL);
- if (branch && call_id && content_type && content)
- {
- slpcall = msn_slpcall_new(slplink);
- slpcall->id = call_id;
- got_invite(slpcall, branch, content_type, content);
- }
- else
+ slpcall = NULL;
+ if (branch && call_id)
{
- g_free(call_id);
- slpcall = NULL;
+ slpcall = msn_slplink_find_slp_call(slplink, call_id);
+ if (slpcall)
+ {
+ g_free(slpcall->branch);
+ slpcall->branch = g_strdup(branch);
+ got_invite(slpcall, branch, content_type, content);
+ }
+ else if (content_type && content)
+ {
+ slpcall = msn_slpcall_new(slplink);
+ slpcall->id = g_strdup(call_id);
+ got_invite(slpcall, branch, content_type, content);
+ }
}
+ g_free(call_id);
g_free(branch);
g_free(content_type);
g_free(content);
@@ -797,38 +1029,31 @@ msn_slp_sip_recv(MsnSlpLink *slplink, const char *body)
g_return_val_if_fail(slpcall != NULL, NULL);
+ content_type = get_token(body, "Content-Type: ", "\r\n");
+
+ content = get_token(body, "\r\n\r\n", NULL);
+
if (strncmp(status, "200 OK", 6))
{
- /* It's not valid. Kill this off. */
- char temp[32];
+ char *error = NULL;
const char *c;
/* Eww */
if ((c = strchr(status, '\r')) || (c = strchr(status, '\n')) ||
(c = strchr(status, '\0')))
{
- size_t offset = c - status;
- if (offset >= sizeof(temp))
- offset = sizeof(temp) - 1;
-
- strncpy(temp, status, offset);
- temp[offset] = '\0';
+ size_t len = c - status;
+ error = g_strndup(status, len);
}
- purple_debug_error("msn", "Received non-OK result: %s\n", temp);
-
- slpcall->wasted = TRUE;
+ got_error(slpcall, error, content_type, content);
+ g_free(error);
- /* msn_slpcall_destroy(slpcall); */
- return slpcall;
+ } else {
+ /* Everything's just dandy */
+ got_ok(slpcall, content_type, content);
}
- content_type = get_token(body, "Content-Type: ", "\r\n");
-
- content = get_token(body, "\r\n\r\n", NULL);
-
- got_ok(slpcall, content_type, content);
-
g_free(content_type);
g_free(content);
}
@@ -860,6 +1085,8 @@ msn_p2p_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
{
MsnSession *session;
MsnSlpLink *slplink;
+ const char *data;
+ gsize len;
session = cmdproc->servconn->session;
slplink = msn_session_get_slplink(session, msg->remote_user);
@@ -882,7 +1109,9 @@ msn_p2p_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
}
}
- msn_slplink_process_msg(slplink, msg);
+ data = msn_message_get_bin_data(msg, &len);
+
+ msn_slplink_process_msg(slplink, &msg->msnslp_header, data, len);
}
static void
@@ -931,6 +1160,8 @@ msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
conv = swboard->conv;
body = msn_message_get_bin_data(msg, &body_len);
+ if (!body || !body_len)
+ return;
body_str = g_strndup(body, body_len);
/* MSN Messenger 7 may send more than one MSNObject in a single message...
@@ -1032,8 +1263,6 @@ msn_release_buddy_icon_request(MsnUserList *userlist)
if (userlist->buddy_icon_window > 0)
{
GQueue *queue;
- PurpleAccount *account;
- const char *username;
queue = userlist->buddy_icon_requests;
@@ -1042,9 +1271,6 @@ msn_release_buddy_icon_request(MsnUserList *userlist)
user = g_queue_pop_head(queue);
- account = userlist->session->account;
- username = user->passport;
-
userlist->buddy_icon_window--;
request_user_display(user);
@@ -1116,31 +1342,21 @@ static void
got_user_display(MsnSlpCall *slpcall,
const guchar *data, gsize size)
{
- MsnUserList *userlist;
+ MsnSlpLink *slplink;
const char *info;
PurpleAccount *account;
g_return_if_fail(slpcall != NULL);
+ slplink = slpcall->slplink;
info = slpcall->data_info;
if (purple_debug_is_verbose())
- purple_debug_info("msn", "Got User Display: %s\n", slpcall->slplink->remote_user);
+ purple_debug_info("msn", "Got User Display: %s\n", slplink->remote_user);
- userlist = slpcall->slplink->session->userlist;
- account = slpcall->slplink->session->account;
+ account = slplink->session->account;
- purple_buddy_icons_set_for_user(account, slpcall->slplink->remote_user,
+ purple_buddy_icons_set_for_user(account, slplink->remote_user,
g_memdup(data, size), size, info);
-
-#if 0
- /* Free one window slot */
- userlist->buddy_icon_window++;
-
- purple_debug_info("msn", "got_user_display(): buddy_icon_window++ yields =%d\n",
- userlist->buddy_icon_window);
-
- msn_release_buddy_icon_request(userlist);
-#endif
}
static void
@@ -1180,6 +1396,26 @@ 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)
+{
+ MsnFetchUserDisplayData *data = user_data;
+ MsnSession *session = data->session;
+
+ session->url_datas = g_slist_remove(session->url_datas, url_data);
+
+ if (url_text) {
+ purple_buddy_icons_set_for_user(session->account, data->remote_user,
+ g_memdup(url_text, len), len,
+ data->sha1);
+ }
+
+ end_user_display(NULL, session);
+
+ g_free(user_data);
+}
+
+static void
request_user_display(MsnUser *user)
{
PurpleAccount *account;
@@ -1200,8 +1436,20 @@ request_user_display(MsnUser *user)
if (g_ascii_strcasecmp(user->passport,
purple_account_get_username(account)))
{
- msn_slplink_request_object(slplink, info, got_user_display,
- end_user_display, obj);
+ const char *url = msn_object_get_url1(obj);
+ if (url) {
+ 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);
+ } else {
+ msn_slplink_request_object(slplink, info, got_user_display,
+ end_user_display, obj);
+ }
}
else
{
diff --git a/libpurple/protocols/msn/slp.h b/libpurple/protocols/msn/slp.h
index a340fbe6c7..4539adfa7f 100644
--- a/libpurple/protocols/msn/slp.h
+++ b/libpurple/protocols/msn/slp.h
@@ -51,6 +51,14 @@ typedef struct
MsnSlpCall * msn_slp_sip_recv(MsnSlpLink *slplink,
const char *body);
+void
+msn_slp_send_ok(MsnSlpCall *slpcall, const char *branch,
+ const char *type, const char *content);
+
+void
+msn_slp_send_decline(MsnSlpCall *slpcall, const char *branch,
+ const char *type, const char *content);
+
void send_bye(MsnSlpCall *slpcall, const char *type);
diff --git a/libpurple/protocols/msn/slpcall.h b/libpurple/protocols/msn/slpcall.h
index 58da0f7e62..fb68c79952 100644
--- a/libpurple/protocols/msn/slpcall.h
+++ b/libpurple/protocols/msn/slpcall.h
@@ -64,6 +64,8 @@ struct _MsnSlpCall
gboolean started; /**< A flag that states if this slpcall's session has
been initiated. */
+ gboolean wait_for_socket;
+
void (*progress_cb)(MsnSlpCall *slpcall,
gsize total_length, gsize len, gsize offset);
void (*session_init_cb)(MsnSlpCall *slpcall);
diff --git a/libpurple/protocols/msn/slplink.c b/libpurple/protocols/msn/slplink.c
index eeeed3090d..ef70203bb1 100644
--- a/libpurple/protocols/msn/slplink.c
+++ b/libpurple/protocols/msn/slplink.c
@@ -78,7 +78,7 @@ msn_slplink_new(MsnSession *session, const char *username)
session->slplinks =
g_list_append(session->slplinks, slplink);
- return slplink;
+ return msn_slplink_ref(slplink);
}
void
@@ -91,15 +91,23 @@ msn_slplink_destroy(MsnSlpLink *slplink)
g_return_if_fail(slplink != NULL);
- if (slplink->swboard != NULL)
+ if (slplink->swboard != NULL) {
slplink->swboard->slplinks = g_list_remove(slplink->swboard->slplinks, slplink);
+ slplink->swboard = NULL;
+ }
+
+ if (slplink->refs > 1) {
+ slplink->refs--;
+ return;
+ }
session = slplink->session;
-#if 0
- if (slplink->directconn != NULL)
- msn_directconn_destroy(slplink->directconn);
-#endif
+ if (slplink->dc != NULL) {
+ slplink->dc->slplink = NULL;
+ msn_dc_destroy(slplink->dc);
+ slplink->dc = NULL;
+ }
while (slplink->slp_calls != NULL)
msn_slpcall_destroy(slplink->slp_calls->data);
@@ -115,6 +123,31 @@ msn_slplink_destroy(MsnSlpLink *slplink)
}
MsnSlpLink *
+msn_slplink_ref(MsnSlpLink *slplink)
+{
+ g_return_val_if_fail(slplink != NULL, NULL);
+
+ slplink->refs++;
+ if (purple_debug_is_verbose())
+ purple_debug_info("msn", "slplink ref (%p)[%d]\n", slplink, slplink->refs);
+
+ return slplink;
+}
+
+void
+msn_slplink_unref(MsnSlpLink *slplink)
+{
+ g_return_if_fail(slplink != NULL);
+
+ slplink->refs--;
+ if (purple_debug_is_verbose())
+ purple_debug_info("msn", "slplink unref (%p)[%d]\n", slplink, slplink->refs);
+
+ if (slplink->refs == 0)
+ msn_slplink_destroy(slplink);
+}
+
+MsnSlpLink *
msn_session_find_slplink(MsnSession *session, const char *who)
{
GList *l;
@@ -155,18 +188,43 @@ msn_slplink_add_slpcall(MsnSlpLink *slplink, MsnSlpCall *slpcall)
slplink->swboard->flag |= MSN_SB_FLAG_FT;
slplink->slp_calls = g_list_append(slplink->slp_calls, slpcall);
+
+ /*
+ if (slplink->dc != NULL && slplink->dc->state == DC_STATE_ESTABLISHED)
+ msn_dc_ref(slplink->dc);
+ */
}
void
msn_slplink_remove_slpcall(MsnSlpLink *slplink, MsnSlpCall *slpcall)
{
+ /*
+ if (slplink->dc != NULL && slplink->dc->state == DC_STATE_ESTABLISHED)
+ msn_dc_unref(slplink->dc);
+ */
+
slplink->slp_calls = g_list_remove(slplink->slp_calls, slpcall);
/* The slplink has no slpcalls in it, release it from MSN_SB_FLAG_FT.
* If nothing else is using it then this might cause swboard to be
* destroyed. */
- if (slplink->slp_calls == NULL && slplink->swboard != NULL)
+ if (slplink->slp_calls == NULL && slplink->swboard != NULL) {
+ slplink->swboard->slplinks = g_list_remove(slplink->swboard->slplinks, slplink);
msn_switchboard_release(slplink->swboard, MSN_SB_FLAG_FT);
+ slplink->swboard = NULL;
+ }
+
+ if (slplink->dc != NULL) {
+ if ((slplink->dc->state != DC_STATE_ESTABLISHED && slplink->dc->slpcall == slpcall)
+ || (slplink->slp_calls == NULL)) {
+ /* The DC is not established and its corresponding slpcall is dead,
+ * or the slplink has no slpcalls in it and no longer needs the DC.
+ */
+ slplink->dc->slplink = NULL;
+ msn_dc_destroy(slplink->dc);
+ slplink->dc = NULL;
+ }
+ }
}
MsnSlpCall *
@@ -206,16 +264,14 @@ msn_slplink_find_slp_call_with_session_id(MsnSlpLink *slplink, long id)
return NULL;
}
-static void
+void
msn_slplink_send_msg(MsnSlpLink *slplink, MsnMessage *msg)
{
-#if 0
- if (slplink->directconn != NULL)
+ if (slplink->dc != NULL && slplink->dc->state == DC_STATE_ESTABLISHED)
{
- msn_directconn_send_msg(slplink->directconn, msg);
+ msn_dc_enqueue_msg(slplink->dc, msg);
}
else
-#endif
{
if (slplink->swboard == NULL)
{
@@ -275,7 +331,7 @@ msn_slplink_send_msgpart(MsnSlpLink *slplink, MsnSlpMessage *slpmsg)
#endif
slpmsg->msgs =
- g_list_append(slpmsg->msgs, msg);
+ g_list_append(slpmsg->msgs, msn_message_ref(msg));
msn_slplink_send_msg(slplink, msg);
if ((slpmsg->flags == 0x20 || slpmsg->flags == 0x1000020 ||
@@ -314,6 +370,7 @@ msg_ack(MsnMessage *msg, void *data)
if (slpmsg->slpcall->xfer && purple_xfer_get_status(slpmsg->slpcall->xfer) == PURPLE_XFER_STATUS_STARTED)
{
slpmsg->slpcall->xfer_msg = slpmsg;
+ msn_message_ref(msg);
purple_xfer_prpl_ready(slpmsg->slpcall->xfer);
}
else
@@ -333,6 +390,8 @@ msg_ack(MsnMessage *msg, void *data)
}
}
}
+
+ msn_message_unref(msg);
}
/* We have received the message nak. */
@@ -346,12 +405,14 @@ msg_nak(MsnMessage *msg, void *data)
msn_slplink_send_msgpart(slpmsg->slplink, slpmsg);
slpmsg->msgs = g_list_remove(slpmsg->msgs, msg);
+ msn_message_unref(msg);
}
static void
msn_slplink_release_slpmsg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg)
{
MsnMessage *msg;
+ const char *passport;
slpmsg->msg = msg = msn_message_new_msnslp();
@@ -390,7 +451,8 @@ msn_slplink_release_slpmsg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg)
msg->msnslp_header.total_size = slpmsg->size;
- msn_message_set_attr(msg, "P2P-Dest", slplink->remote_user);
+ passport = purple_normalize(slplink->session->account, slplink->remote_user);
+ msn_message_set_attr(msg, "P2P-Dest", passport);
msg->ack_cb = msg_ack;
msg->nak_cb = msg_nak;
@@ -431,21 +493,29 @@ msn_slplink_send_queued_slpmsgs(MsnSlpLink *slplink)
}
}
-static void
-msn_slplink_send_ack(MsnSlpLink *slplink, MsnMessage *msg)
+static MsnSlpMessage *
+msn_slplink_create_ack(MsnSlpLink *slplink, MsnSlpHeader *header)
{
MsnSlpMessage *slpmsg;
slpmsg = msn_slpmsg_new(slplink);
- slpmsg->session_id = msg->msnslp_header.session_id;
- slpmsg->size = msg->msnslp_header.total_size;
+ slpmsg->session_id = header->session_id;
+ slpmsg->size = header->total_size;
slpmsg->flags = 0x02;
- slpmsg->ack_id = msg->msnslp_header.id;
- slpmsg->ack_sub_id = msg->msnslp_header.ack_id;
- slpmsg->ack_size = msg->msnslp_header.total_size;
+ slpmsg->ack_id = header->id;
+ slpmsg->ack_sub_id = header->ack_id;
+ slpmsg->ack_size = header->total_size;
slpmsg->info = "SLP ACK";
+ return slpmsg;
+}
+
+static void
+msn_slplink_send_ack(MsnSlpLink *slplink, MsnSlpHeader *header)
+{
+ MsnSlpMessage *slpmsg = msn_slplink_create_ack(slplink, header);
+
msn_slplink_send_slpmsg(slplink, slpmsg);
msn_slpmsg_destroy(slpmsg);
}
@@ -457,6 +527,9 @@ send_file_cb(MsnSlpCall *slpcall)
PurpleXfer *xfer;
xfer = (PurpleXfer *)slpcall->xfer;
+ if (purple_xfer_get_status(xfer) >= PURPLE_XFER_STATUS_STARTED)
+ return;
+
purple_xfer_ref(xfer);
purple_xfer_start(xfer, -1, NULL, 0);
if (purple_xfer_get_status(xfer) != PURPLE_XFER_STATUS_STARTED) {
@@ -491,38 +564,27 @@ msn_slplink_message_find(MsnSlpLink *slplink, long session_id, long id)
}
void
-msn_slplink_process_msg(MsnSlpLink *slplink, MsnMessage *msg)
+msn_slplink_process_msg(MsnSlpLink *slplink, MsnSlpHeader *header, const char *data, gsize len)
{
MsnSlpMessage *slpmsg;
- const char *data;
guint64 offset;
- gsize len;
PurpleXfer *xfer = NULL;
- if (purple_debug_is_verbose())
- msn_slpmsg_show(msg);
-
-#ifdef MSN_DEBUG_SLP_FILES
- debug_msg_to_file(msg, FALSE);
-#endif
-
- if (msg->msnslp_header.total_size < msg->msnslp_header.length)
+ if (header->total_size < header->length)
{
purple_debug_error("msn", "This can't be good\n");
g_return_if_reached();
}
- data = msn_message_get_bin_data(msg, &len);
-
- offset = msg->msnslp_header.offset;
+ offset = header->offset;
if (offset == 0)
{
slpmsg = msn_slpmsg_new(slplink);
- slpmsg->id = msg->msnslp_header.id;
- slpmsg->session_id = msg->msnslp_header.session_id;
- slpmsg->size = msg->msnslp_header.total_size;
- slpmsg->flags = msg->msnslp_header.flags;
+ slpmsg->id = header->id;
+ slpmsg->session_id = header->session_id;
+ slpmsg->size = header->total_size;
+ slpmsg->flags = header->flags;
if (slpmsg->session_id)
{
@@ -567,10 +629,10 @@ msn_slplink_process_msg(MsnSlpLink *slplink, MsnMessage *msg)
}
else
{
- slpmsg = msn_slplink_message_find(slplink, msg->msnslp_header.session_id, msg->msnslp_header.id);
+ slpmsg = msn_slplink_message_find(slplink, header->session_id, header->id);
if (slpmsg == NULL)
{
- /* Probably the transfer was canceled */
+ /* Probably the transfer was cancelled */
purple_debug_error("msn", "Couldn't find slpmsg\n");
return;
}
@@ -615,8 +677,7 @@ msn_slplink_process_msg(MsnSlpLink *slplink, MsnMessage *msg)
return;
#endif
- if (msg->msnslp_header.offset + msg->msnslp_header.length
- >= msg->msnslp_header.total_size)
+ if (header->offset + header->length >= header->total_size)
{
/* All the pieces of the slpmsg have been received */
MsnSlpCall *slpcall;
@@ -628,32 +689,45 @@ msn_slplink_process_msg(MsnSlpLink *slplink, MsnMessage *msg)
return;
}
- if (!slpcall->wasted) {
- if (slpmsg->flags == 0x100)
- {
- MsnDirectConn *directconn;
+ purple_debug_info("msn", "msn_slplink_process_msg: slpmsg complete\n");
- directconn = slplink->directconn;
+ if (/* !slpcall->wasted && */ slpmsg->flags == 0x100)
+ {
#if 0
- if (!directconn->acked)
- msn_directconn_send_handshake(directconn);
-#endif
- }
- else if (slpmsg->flags == 0x00 || slpmsg->flags == 0x1000000 ||
- slpmsg->flags == 0x20 || slpmsg->flags == 0x1000020 ||
- slpmsg->flags == 0x1000030)
- {
- /* Release all the messages and send the ACK */
+ MsnDirectConn *directconn;
- msn_slplink_send_ack(slplink, msg);
+ directconn = slplink->directconn;
+ if (!directconn->acked)
+ msn_directconn_send_handshake(directconn);
+#endif
+ }
+ else if (slpmsg->flags == 0x00 || slpmsg->flags == 0x1000000 ||
+ slpmsg->flags == 0x20 || slpmsg->flags == 0x1000020 ||
+ slpmsg->flags == 0x1000030)
+ {
+ /* Release all the messages and send the ACK */
+
+ if (slpcall->wait_for_socket) {
+ /*
+ * Save ack for later because we have to send
+ * a 200 OK message to the previous direct connect
+ * invitation before ACK but the listening socket isn't
+ * created yet.
+ */
+ purple_debug_info("msn", "msn_slplink_process_msg: save ACK\n");
+
+ slpcall->slplink->dc->prev_ack = msn_slplink_create_ack(slplink, header);
+ } else if (!slpcall->wasted) {
+ purple_debug_info("msn", "msn_slplink_process_msg: send ACK\n");
+
+ msn_slplink_send_ack(slplink, header);
msn_slplink_send_queued_slpmsgs(slplink);
}
-
}
msn_slpmsg_destroy(slpmsg);
- if (slpcall->wasted)
+ if (!slpcall->wait_for_socket && slpcall->wasted)
msn_slpcall_destroy(slpcall);
}
}
@@ -662,15 +736,19 @@ static gchar *
gen_context(PurpleXfer *xfer, const char *file_name, const char *file_path)
{
gsize size = 0;
- MsnFileContext header;
+ MsnFileContext *header;
gchar *u8 = NULL;
gchar *ret;
gunichar2 *uni = NULL;
glong currentChar = 0;
glong len = 0;
+ const char *preview;
+ gsize preview_len;
size = purple_xfer_get_size(xfer);
+ purple_xfer_prepare_thumbnail(xfer, "png");
+
if (!file_name) {
gchar *basename = g_path_get_basename(file_path);
u8 = purple_utf8_try_convert(basename);
@@ -686,23 +764,33 @@ gen_context(PurpleXfer *xfer, const char *file_name, const char *file_path)
u8 = NULL;
}
- header.length = GUINT32_TO_LE(sizeof(MsnFileContext) - 1);
- header.version = GUINT32_TO_LE(2); /* V.3 contains additional unnecessary data */
- header.file_size = GUINT64_TO_LE(size);
- header.type = GUINT32_TO_LE(1); /* No file preview */
+ preview = purple_xfer_get_thumbnail(xfer, &preview_len);
+ header = g_malloc(sizeof(MsnFileContext) + preview_len);
+
+ header->length = GUINT32_TO_LE(sizeof(MsnFileContext) - 1);
+ header->version = GUINT32_TO_LE(2); /* V.3 contains additional unnecessary data */
+ header->file_size = GUINT64_TO_LE(size);
+ if (preview)
+ header->type = GUINT32_TO_LE(0);
+ else
+ header->type = GUINT32_TO_LE(1);
len = MIN(len, MAX_FILE_NAME_LEN);
for (currentChar = 0; currentChar < len; currentChar++) {
- header.file_name[currentChar] = GUINT16_TO_LE(uni[currentChar]);
+ header->file_name[currentChar] = GUINT16_TO_LE(uni[currentChar]);
}
- memset(&header.file_name[currentChar], 0x00, (MAX_FILE_NAME_LEN - currentChar) * 2);
+ memset(&header->file_name[currentChar], 0x00, (MAX_FILE_NAME_LEN - currentChar) * 2);
- memset(&header.unknown1, 0, sizeof(header.unknown1));
- header.unknown2 = GUINT32_TO_LE(0xffffffff);
- header.preview[0] = '\0';
+ memset(&header->unknown1, 0, sizeof(header->unknown1));
+ header->unknown2 = GUINT32_TO_LE(0xffffffff);
+ if (preview) {
+ memcpy(&header->preview, preview, preview_len);
+ }
+ header->preview[preview_len] = '\0';
g_free(uni);
- ret = purple_base64_encode((const guchar *)&header, sizeof(MsnFileContext));
+ ret = purple_base64_encode((const guchar *)header, sizeof(MsnFileContext) + preview_len);
+ g_free(header);
return ret;
}
diff --git a/libpurple/protocols/msn/slplink.h b/libpurple/protocols/msn/slplink.h
index 4059ea9216..e3b2e97de0 100644
--- a/libpurple/protocols/msn/slplink.h
+++ b/libpurple/protocols/msn/slplink.h
@@ -42,19 +42,23 @@ struct _MsnSlpLink
{
MsnSession *session;
MsnSwitchBoard *swboard;
+ MsnDirectConn *dc;
+
+ int refs;
char *remote_user;
int slp_seq_id;
- MsnDirectConn *directconn;
-
GList *slp_calls;
GList *slp_msgs;
GQueue *slp_msg_queue;
};
+MsnSlpLink *msn_slplink_ref(MsnSlpLink *slplink);
+void msn_slplink_unref(MsnSlpLink *slplink);
+
void msn_slplink_destroy(MsnSlpLink *slplink);
/**
@@ -79,9 +83,10 @@ void msn_slplink_queue_slpmsg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg);
void msn_slplink_send_slpmsg(MsnSlpLink *slplink,
MsnSlpMessage *slpmsg);
void msn_slplink_send_queued_slpmsgs(MsnSlpLink *slplink);
-void msn_slplink_process_msg(MsnSlpLink *slplink, MsnMessage *msg);
+void msn_slplink_process_msg(MsnSlpLink *slplink, MsnSlpHeader *header, const char *data, gsize len);
void msn_slplink_request_ft(MsnSlpLink *slplink, PurpleXfer *xfer);
+void msn_slplink_send_msg(MsnSlpLink *slplink, MsnMessage *msg);
/* Only exported for msn_xfer_write */
void msn_slplink_send_msgpart(MsnSlpLink *slplink, MsnSlpMessage *slpmsg);
diff --git a/libpurple/protocols/msn/slpmsg.c b/libpurple/protocols/msn/slpmsg.c
index 3864ae1159..4b3f96a926 100644
--- a/libpurple/protocols/msn/slpmsg.c
+++ b/libpurple/protocols/msn/slpmsg.c
@@ -67,7 +67,7 @@ msn_slpmsg_destroy(MsnSlpMessage *slpmsg)
if (slpmsg->img == NULL)
g_free(slpmsg->buffer);
- for (cur = slpmsg->msgs; cur != NULL; cur = cur->next)
+ for (cur = slpmsg->msgs; cur != NULL; cur = g_list_delete_link(cur, cur))
{
/* Something is pointing to this slpmsg, so we should remove that
* pointer to prevent a crash. */
@@ -78,8 +78,8 @@ msn_slpmsg_destroy(MsnSlpMessage *slpmsg)
msg->ack_cb = NULL;
msg->nak_cb = NULL;
msg->ack_data = NULL;
+ msn_message_unref(msg);
}
- g_list_free(slpmsg->msgs);
slplink->slp_msgs = g_list_remove(slplink->slp_msgs, slpmsg);
diff --git a/libpurple/protocols/msn/soap.c b/libpurple/protocols/msn/soap.c
index c7d2a64075..87071aa28c 100644
--- a/libpurple/protocols/msn/soap.c
+++ b/libpurple/protocols/msn/soap.c
@@ -68,7 +68,6 @@ typedef struct _MsnSoapConnection {
GQueue *queue;
MsnSoapRequest *current_request;
- gboolean unsafe_debug;
} MsnSoapConnection;
static gboolean msn_soap_connection_run(gpointer data);
@@ -80,7 +79,6 @@ msn_soap_connection_new(MsnSession *session, const char *host)
conn->session = session;
conn->host = g_strdup(host);
conn->queue = g_queue_new();
- conn->unsafe_debug = purple_debug_is_unsafe();
return conn;
}
@@ -509,7 +507,7 @@ msn_soap_read_cb(gpointer data, gint fd, PurpleInputCondition cond)
purple_debug_info("soap", "read: %s\n", g_strerror(perrno));
if (conn->current_request && conn->current_request->secure &&
- !conn->unsafe_debug)
+ !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);
@@ -659,7 +657,7 @@ msn_soap_connection_run(gpointer data)
g_string_append(conn->buf, "\r\n");
g_string_append(conn->buf, body);
- if (req->secure && !conn->unsafe_debug)
+ 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);
diff --git a/libpurple/protocols/msn/state.c b/libpurple/protocols/msn/state.c
index 319f568e4e..536a36e33d 100644
--- a/libpurple/protocols/msn/state.c
+++ b/libpurple/protocols/msn/state.c
@@ -260,7 +260,7 @@ msn_change_status(MsnSession *session)
if (msnobj == NULL)
{
- msn_cmdproc_send(cmdproc, "CHG", "%s %d", state_text, caps);
+ msn_cmdproc_send(cmdproc, "CHG", "%s %u", state_text, caps);
}
else
{
@@ -268,7 +268,7 @@ msn_change_status(MsnSession *session)
msnobj_str = msn_object_to_string(msnobj);
- msn_cmdproc_send(cmdproc, "CHG", "%s %d %s", state_text,
+ msn_cmdproc_send(cmdproc, "CHG", "%s %u %s", state_text,
caps, purple_url_encode(msnobj_str));
g_free(msnobj_str);
diff --git a/libpurple/protocols/msn/switchboard.c b/libpurple/protocols/msn/switchboard.c
index 6b255355de..5f6e37b836 100644
--- a/libpurple/protocols/msn/switchboard.c
+++ b/libpurple/protocols/msn/switchboard.c
@@ -87,8 +87,17 @@ msn_switchboard_destroy(MsnSwitchBoard *swboard)
purple_timeout_remove(swboard->reconn_timeout_h);
/* If it linked us is because its looking for trouble */
- while (swboard->slplinks != NULL)
- msn_slplink_destroy(swboard->slplinks->data);
+ while (swboard->slplinks != NULL) {
+ MsnSlpLink *slplink = swboard->slplinks->data;
+
+ /* Destroy only those slplinks which use the switchboard */
+ if (slplink->dc == NULL)
+ msn_slplink_destroy(slplink);
+ else {
+ swboard->slplinks = g_list_remove(swboard->slplinks, slplink);
+ slplink->swboard = NULL;
+ }
+ }
/* Destroy the message queue */
while ((msg = g_queue_pop_head(swboard->msg_queue)) != NULL)
@@ -731,8 +740,13 @@ bye_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
else if ((swboard->current_users > 1) ||
(purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT))
{
+ GList *passport;
/* This is a switchboard used for a chat */
purple_conv_chat_remove_user(PURPLE_CONV_CHAT(swboard->conv), user, NULL);
+
+ passport = g_list_find_custom(swboard->users, user, (GCompareFunc)strcmp);
+ g_free(passport->data);
+ swboard->users = g_list_delete_link(swboard->users, passport);
swboard->current_users--;
if (swboard->current_users == 0)
msn_switchboard_destroy(swboard);
@@ -747,12 +761,8 @@ bye_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
static void
iro_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
- PurpleAccount *account;
- PurpleConnection *gc;
MsnSwitchBoard *swboard;
- account = cmdproc->session->account;
- gc = account->gc;
swboard = cmdproc->data;
swboard->total_users = atoi(cmd->params[2]);
@@ -764,16 +774,12 @@ static void
joi_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
MsnSession *session;
- PurpleAccount *account;
- PurpleConnection *gc;
MsnSwitchBoard *swboard;
const char *passport;
passport = cmd->params[0];
session = cmdproc->session;
- account = session->account;
- gc = account->gc;
swboard = cmdproc->data;
msn_switchboard_add_user(swboard, passport);
@@ -937,7 +943,7 @@ msn_switchboard_show_ink(MsnSwitchBoard *swboard, const char *passport,
}
imgid = purple_imgstore_add_with_id(image_data, image_len, NULL);
- image_msg = g_strdup_printf("<IMG ID='%d'/>", imgid);
+ image_msg = g_strdup_printf("<IMG ID='%d'>", imgid);
if (swboard->current_users > 1 ||
((swboard->conv != NULL) &&
diff --git a/libpurple/protocols/msn/user.h b/libpurple/protocols/msn/user.h
index 48e896fa06..49fa34ec1e 100644
--- a/libpurple/protocols/msn/user.h
+++ b/libpurple/protocols/msn/user.h
@@ -57,8 +57,8 @@ typedef enum
* put this info in in a separate struct to save memory because we
* allocate an MsnUser struct for each buddy, but we generally only
* need this information for a small percentage of our buddies
- * (usually less than 1%). Putting it in a separate struct saves
- * makes MsnUser smaller by the size of a few pointers.
+ * (usually less than 1%). Putting it in a separate struct makes
+ * MsnUser smaller by the size of a few pointers.
*/
typedef struct _MsnUserExtendedInfo
{
diff --git a/libpurple/protocols/mxit/actions.c b/libpurple/protocols/mxit/actions.c
index b92d7162d6..a8320c45b9 100644
--- a/libpurple/protocols/mxit/actions.c
+++ b/libpurple/protocols/mxit/actions.c
@@ -35,89 +35,6 @@
#include "profile.h"
-/* MXit Moods */
-static const char* moods[] = {
- /* 0 */ N_("None"),
- /* 1 */ N_("Angry"),
- /* 2 */ N_("Excited"),
- /* 3 */ N_("Grumpy"),
- /* 4 */ N_("Happy"),
- /* 5 */ N_("In Love"),
- /* 6 */ N_("Invincible"),
- /* 7 */ N_("Sad"),
- /* 8 */ N_("Hot"),
- /* 9 */ N_("Sick"),
- /* 10 */ N_("Sleepy")
-};
-
-
-/*------------------------------------------------------------------------
- * The user has selected to change their current mood.
- *
- * @param gc The connection object
- * @param fields The fields from the request pop-up
- */
-static void mxit_cb_set_mood( PurpleConnection* gc, PurpleRequestFields* fields )
-{
- struct MXitSession* session = (struct MXitSession*) gc->proto_data;
- int mood = purple_request_fields_get_choice( fields, "mood" );
-
- purple_debug_info( MXIT_PLUGIN_ID, "mxit_cb_set_mood (%i)\n", mood );
-
- if ( !PURPLE_CONNECTION_IS_VALID( gc ) ) {
- purple_debug_error( MXIT_PLUGIN_ID, "Unable to set mood; account offline.\n" );
- return;
- }
-
- /* Save the new mood in session */
- session->mood = mood;
-
- /* now send the update to MXit */
- mxit_send_mood( session, mood );
-}
-
-
-/*------------------------------------------------------------------------
- * Create and display the mood selection window to the user.
- *
- * @param action The action object
- */
-static void mxit_cb_action_mood( PurplePluginAction* action )
-{
- PurpleConnection* gc = (PurpleConnection*) action->context;
- struct MXitSession* session = (struct MXitSession*) gc->proto_data;
-
- PurpleRequestFields* fields = NULL;
- PurpleRequestFieldGroup* group = NULL;
- PurpleRequestField* field = NULL;
- unsigned int i = 0;
-
- purple_debug_info( MXIT_PLUGIN_ID, "mxit_cb_action_mood\n" );
-
- fields = purple_request_fields_new();
- group = purple_request_field_group_new( NULL );
- purple_request_fields_add_group( fields, group );
-
- /* show current mood */
- field = purple_request_field_string_new( "current", _( "Current Mood" ), _( moods[session->mood] ), FALSE );
- purple_request_field_string_set_editable( field, FALSE ); /* current mood field is not editable */
- purple_request_field_group_add_field( group, field );
-
- /* add all moods to list */
- field = purple_request_field_choice_new( "mood", _( "New Mood" ), 0 );
- for ( i = 0; i < ARRAY_SIZE( moods ); i++ ) {
- purple_request_field_choice_add( field, _( moods[i] ) );
- }
- purple_request_field_set_required( field, TRUE );
- purple_request_field_choice_set_default_value( field, session->mood );
- purple_request_field_group_add_field( group, field );
-
- /* (reference: "libpurple/request.h") */
- purple_request_fields( gc, _( "Mood" ), _( "Change your Mood" ), _( "How do you feel right now?" ), fields, _( "Set" ),
- G_CALLBACK( mxit_cb_set_mood ), _( "Cancel" ), NULL, purple_connection_get_account( gc ), NULL, NULL, gc );
-}
-
-
/*------------------------------------------------------------------------
* The user has selected to change their profile.
*
@@ -169,7 +86,7 @@ static void mxit_cb_set_profile( PurpleConnection* gc, PurpleRequestFields* fiel
/* validate name */
name = purple_request_fields_get_string( fields, "name" );
if ( ( !name ) || ( strlen( name ) < 3 ) ) {
- err = _( "The name you entered is invalid." );
+ err = _( "The Display Name you entered is invalid." );
goto out;
}
@@ -196,26 +113,26 @@ out:
/* update name */
g_strlcpy( profile->nickname, name, sizeof( profile->nickname ) );
- g_snprintf( attrib, sizeof( attrib ), "\01%s\01%i\01%s", CP_PROFILE_FULLNAME, CP_PROF_TYPE_UTF8, profile->nickname );
+ g_snprintf( attrib, sizeof( attrib ), "\01%s\01%i\01%s", CP_PROFILE_FULLNAME, CP_PROFILE_TYPE_UTF8, profile->nickname );
g_string_append( attributes, attrib );
acount++;
/* update hidden */
field = purple_request_fields_get_field( fields, "hidden" );
profile->hidden = purple_request_field_bool_get_value( field );
- g_snprintf( attrib, sizeof( attrib ), "\01%s\01%i\01%s", CP_PROFILE_HIDENUMBER, CP_PROF_TYPE_BOOL, ( profile->hidden ) ? "1" : "0" );
+ g_snprintf( attrib, sizeof( attrib ), "\01%s\01%i\01%s", CP_PROFILE_HIDENUMBER, CP_PROFILE_TYPE_BOOL, ( profile->hidden ) ? "1" : "0" );
g_string_append( attributes, attrib );
acount++;
/* update birthday */
- strcpy( profile->birthday, bday );
- g_snprintf( attrib, sizeof( attrib ), "\01%s\01%i\01%s", CP_PROFILE_BIRTHDATE, CP_PROF_TYPE_UTF8, profile->birthday );
+ g_strlcpy( profile->birthday, bday, sizeof( profile->birthday ) );
+ g_snprintf( attrib, sizeof( attrib ), "\01%s\01%i\01%s", CP_PROFILE_BIRTHDATE, CP_PROFILE_TYPE_UTF8, profile->birthday );
g_string_append( attributes, attrib );
acount++;
/* update gender */
profile->male = ( purple_request_fields_get_choice( fields, "male" ) != 0 );
- g_snprintf( attrib, sizeof( attrib ), "\01%s\01%i\01%s", CP_PROFILE_GENDER, CP_PROF_TYPE_BOOL, ( profile->male ) ? "1" : "0" );
+ g_snprintf( attrib, sizeof( attrib ), "\01%s\01%i\01%s", CP_PROFILE_GENDER, CP_PROFILE_TYPE_BOOL, ( profile->male ) ? "1" : "0" );
g_string_append( attributes, attrib );
acount++;
@@ -224,8 +141,8 @@ out:
if ( !name )
profile->title[0] = '\0';
else
- strcpy( profile->title, name );
- g_snprintf( attrib, sizeof( attrib ), "\01%s\01%i\01%s", CP_PROFILE_TITLE, CP_PROF_TYPE_UTF8, profile->title );
+ g_strlcpy( profile->title, name, sizeof( profile->title ) );
+ g_snprintf( attrib, sizeof( attrib ), "\01%s\01%i\01%s", CP_PROFILE_TITLE, CP_PROFILE_TYPE_UTF8, profile->title );
g_string_append( attributes, attrib );
acount++;
@@ -234,8 +151,8 @@ out:
if ( !name )
profile->firstname[0] = '\0';
else
- strcpy( profile->firstname, name );
- g_snprintf( attrib, sizeof( attrib ), "\01%s\01%i\01%s", CP_PROFILE_FIRSTNAME, CP_PROF_TYPE_UTF8, profile->firstname );
+ g_strlcpy( profile->firstname, name, sizeof( profile->firstname ) );
+ g_snprintf( attrib, sizeof( attrib ), "\01%s\01%i\01%s", CP_PROFILE_FIRSTNAME, CP_PROFILE_TYPE_UTF8, profile->firstname );
g_string_append( attributes, attrib );
acount++;
@@ -244,8 +161,8 @@ out:
if ( !name )
profile->lastname[0] = '\0';
else
- strcpy( profile->lastname, name );
- g_snprintf( attrib, sizeof( attrib ), "\01%s\01%i\01%s", CP_PROFILE_LASTNAME, CP_PROF_TYPE_UTF8, profile->lastname );
+ g_strlcpy( profile->lastname, name, sizeof( profile->lastname ) );
+ g_snprintf( attrib, sizeof( attrib ), "\01%s\01%i\01%s", CP_PROFILE_LASTNAME, CP_PROFILE_TYPE_UTF8, profile->lastname );
g_string_append( attributes, attrib );
acount++;
@@ -254,8 +171,8 @@ out:
if ( !name )
profile->email[0] = '\0';
else
- strcpy( profile->email, name );
- g_snprintf( attrib, sizeof( attrib ), "\01%s\01%i\01%s", CP_PROFILE_EMAIL, CP_PROF_TYPE_UTF8, profile->email );
+ g_strlcpy( profile->email, name, sizeof( profile->email ) );
+ g_snprintf( attrib, sizeof( attrib ), "\01%s\01%i\01%s", CP_PROFILE_EMAIL, CP_PROFILE_TYPE_UTF8, profile->email );
g_string_append( attributes, attrib );
acount++;
@@ -264,8 +181,8 @@ out:
if ( !name )
profile->mobilenr[0] = '\0';
else
- strcpy( profile->mobilenr, name );
- g_snprintf( attrib, sizeof( attrib ), "\01%s\01%i\01%s", CP_PROFILE_MOBILENR, CP_PROF_TYPE_UTF8, profile->mobilenr );
+ g_strlcpy( profile->mobilenr, name, sizeof( profile->mobilenr ) );
+ g_snprintf( attrib, sizeof( attrib ), "\01%s\01%i\01%s", CP_PROFILE_MOBILENR, CP_PROFILE_TYPE_UTF8, profile->mobilenr );
g_string_append( attributes, attrib );
acount++;
@@ -308,6 +225,15 @@ static void mxit_cb_action_profile( PurplePluginAction* action )
group = purple_request_field_group_new( NULL );
purple_request_fields_add_group( fields, group );
+#if 0
+ /* UID (read-only) */
+ if ( session->uid ) {
+ field = purple_request_field_string_new( "mxitid", _( "Your UID" ), session->uid, FALSE );
+ purple_request_field_string_set_editable( field, FALSE );
+ purple_request_field_group_add_field( group, field );
+ }
+#endif
+
/* pin */
field = purple_request_field_string_new( "pin", _( "PIN" ), session->acc->password, FALSE );
purple_request_field_string_set_masked( field, TRUE );
@@ -323,6 +249,8 @@ static void mxit_cb_action_profile( PurplePluginAction* action )
/* birthday */
field = purple_request_field_string_new( "bday", _( "Birthday" ), profile->birthday, FALSE );
purple_request_field_group_add_field( group, field );
+ if ( profile->flags & CP_PROF_DOBLOCKED )
+ purple_request_field_string_set_editable( field, FALSE );
/* gender */
field = purple_request_field_choice_new( "male", _( "Gender" ), ( profile->male ) ? 1 : 0 );
@@ -335,7 +263,7 @@ static void mxit_cb_action_profile( PurplePluginAction* action )
purple_request_field_group_add_field( group, field );
/* title */
- field = purple_request_field_string_new( "title", _( "Job Title" ), profile->title, FALSE );
+ field = purple_request_field_string_new( "title", _( "Title" ), profile->title, FALSE );
purple_request_field_group_add_field( group, field );
/* first name */
@@ -387,11 +315,12 @@ static void mxit_cb_action_about( PurplePluginAction* action )
char version[256];
g_snprintf( version, sizeof( version ), "MXit libPurple Plugin v%s\n"
- "MXit Client Protocol v%s\n\n"
+ "MXit Client Protocol v%i.%i\n\n"
"Author:\nPieter Loubser\n\n"
"Contributors:\nAndrew Victor\n\n"
"Testers:\nBraeme Le Roux\n\n",
- MXIT_PLUGIN_VERSION, MXIT_CP_RELEASE );
+ MXIT_PLUGIN_VERSION,
+ ( MXIT_CP_PROTO_VESION / 10 ), ( MXIT_CP_PROTO_VESION % 10 ) );
mxit_popup( PURPLE_NOTIFY_MSG_INFO, _( "About" ), version );
}
@@ -409,10 +338,6 @@ GList* mxit_actions( PurplePlugin* plugin, gpointer context )
PurplePluginAction* action = NULL;
GList* m = NULL;
- /* display / change mood */
- action = purple_plugin_action_new( _( "Change Mood..." ), mxit_cb_action_mood );
- m = g_list_append( m, action );
-
/* display / change profile */
action = purple_plugin_action_new( _( "Change Profile..." ), mxit_cb_action_profile );
m = g_list_append( m, action );
diff --git a/libpurple/protocols/mxit/chunk.h b/libpurple/protocols/mxit/chunk.h
index 199388bb35..98fd4eee52 100644
--- a/libpurple/protocols/mxit/chunk.h
+++ b/libpurple/protocols/mxit/chunk.h
@@ -42,7 +42,7 @@
#define CP_CHUNK_OFFER 0x06 /* (6) offer file */
#define CP_CHUNK_REJECT 0x07 /* (7) reject file */
#define CP_CHUNK_GET 0x08 /* (8) get file */
-#define CP_CHUNK_RECIEVED 0x09 /* (9) received file */
+#define CP_CHUNK_RECEIVED 0x09 /* (9) received file */
#define CP_CHUNK_DIRECT_SND 0x0A /* (10) send file direct */
#define CP_CHUNK_DIRECT_FWD 0x0B /* (11) forward file direct */
#define CP_CHUNK_SKIN 0x0C /* (12) MXit client skin */
diff --git a/libpurple/protocols/mxit/formcmds.c b/libpurple/protocols/mxit/formcmds.c
index 7948f4bd5b..ffbdb5fa50 100644
--- a/libpurple/protocols/mxit/formcmds.c
+++ b/libpurple/protocols/mxit/formcmds.c
@@ -42,7 +42,7 @@
typedef enum
{
MXIT_CMD_UNKNOWN = 0, /* Unknown command */
- MXIT_CMD_CLRSCR, /* Clear screen (clrmsgscreen) */
+ MXIT_CMD_CLEAR, /* Clear (clear) */
MXIT_CMD_SENDSMS, /* Send SMS (sendsms) */
MXIT_CMD_REPLY, /* Reply (reply) */
MXIT_CMD_PLATREQ, /* Platform Request (platreq) */
@@ -138,8 +138,8 @@ static MXitCommandType command_type(GHashTable* hash)
type = g_hash_table_lookup(hash, "type");
if (type == NULL) /* no command provided */
return MXIT_CMD_UNKNOWN;
- else if (strcmp(type, "clrmsgscreen") == 0) /* clear the screen */
- return MXIT_CMD_CLRSCR;
+ else if (strcmp(type, "clear") == 0) /* clear */
+ return MXIT_CMD_CLEAR;
else if (strcmp(type, "sendsms") == 0) /* send an SMS */
return MXIT_CMD_SENDSMS;
else if (strcmp(type, "reply") == 0) /* list of options */
@@ -205,27 +205,38 @@ static GHashTable* command_tokenize(char* cmd)
/*------------------------------------------------------------------------
- * Process a ClearScreen MXit command.
+ * Process a Clear MXit command.
+ * [::op=cmd|type=clear|clearmsgscreen=true|auto=true|id=12345:]
*
- * @param session The MXit session object
- * @param from The sender of the message.
+ * @param session The MXit session object
+ * @param from The sender of the message.
+ * @param hash The MXit command <key,value> map
*/
-static void command_clearscreen(struct MXitSession* session, const char* from)
+static void command_clear(struct MXitSession* session, const char* from, GHashTable* hash)
{
PurpleConversation *conv;
+ char* clearmsgscreen;
+
+ conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, from, session->acc);
+ if (conv == NULL) {
+ purple_debug_error(MXIT_PLUGIN_ID, _( "Conversation with '%s' not found\n" ), from);
+ return;
+ }
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, from, session->acc);
- if (conv == NULL) {
- purple_debug_error(MXIT_PLUGIN_ID, _( "Conversation with '%s' not found\n" ), from);
- return;
- }
+ clearmsgscreen = g_hash_table_lookup(hash, "clearmsgscreen");
+ if ( (clearmsgscreen) && (strcmp(clearmsgscreen, "true") == 0) ) {
+ /* this is a command to clear the chat screen */
+ purple_debug_info(MXIT_PLUGIN_ID, "Clear the screen\n");
- purple_conversation_clear_message_history(conv); // TODO: This doesn't actually clear the screen.
+ purple_conversation_clear_message_history(conv); // TODO: This doesn't actually clear the screen.
+ }
}
/*------------------------------------------------------------------------
* Process a Reply MXit command.
+ * [::op=cmd|type=reply|replymsg=back|selmsg=b) Back|id=12345:]
+ * [::op=cmd|nm=rep|type=reply|replymsg=back|selmsg=b) Back|id=12345:]
*
* @param mx The received message data object
* @param hash The MXit command <key,value> map
@@ -234,10 +245,21 @@ static void command_reply(struct RXMsgData* mx, GHashTable* hash)
{
char* replymsg;
char* selmsg;
+ char* nm;
selmsg = g_hash_table_lookup(hash, "selmsg"); /* find the selection message */
replymsg = g_hash_table_lookup(hash, "replymsg"); /* find the reply message */
- if ((selmsg) && (replymsg)) {
+ nm = g_hash_table_lookup(hash, "nm"); /* name parameter */
+ if ((selmsg) && (replymsg) && (nm)) {
+ gchar* seltext = g_markup_escape_text(purple_url_decode(selmsg), -1);
+ gchar* replycmd = g_strdup_printf("::type=reply|nm=%s|res=%s|err=0:", nm, replymsg);
+
+ mxit_add_html_link( mx, replycmd, seltext );
+
+ g_free(seltext);
+ g_free(replycmd);
+ }
+ else if ((selmsg) && (replymsg)) {
gchar* seltext = g_markup_escape_text(purple_url_decode(selmsg), -1);
mxit_add_html_link( mx, purple_url_decode(replymsg), seltext );
@@ -366,8 +388,8 @@ int mxit_parse_command(struct RXMsgData* mx, char* message)
MXitCommandType type = command_type(hash);
switch (type) {
- case MXIT_CMD_CLRSCR :
- command_clearscreen(mx->session, mx->from);
+ case MXIT_CMD_CLEAR :
+ command_clear(mx->session, mx->from, hash);
break;
case MXIT_CMD_REPLY :
command_reply(mx, hash);
diff --git a/libpurple/protocols/mxit/http.c b/libpurple/protocols/mxit/http.c
index 2ec53cb7d5..fcfcf54071 100644
--- a/libpurple/protocols/mxit/http.c
+++ b/libpurple/protocols/mxit/http.c
@@ -118,7 +118,7 @@ static void mxit_cb_http_read( gpointer user_data, gint source, PurpleInputCondi
/* read bytes from the socket */
len = read( session->fd, buf + buflen, sizeof( buf ) - buflen );
if ( len <= 0 ) {
- /* connection has been terminated, or error occured */
+ /* connection has been terminated, or error occurred */
goto done;
}
@@ -139,7 +139,7 @@ static void mxit_cb_http_read( gpointer user_data, gint source, PurpleInputCondi
}
buflen += len;
- /* we have the header's end now skip over the http seperator to get the body offset */
+ /* 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;
@@ -206,7 +206,7 @@ static void mxit_cb_http_read( gpointer user_data, gint source, PurpleInputCondi
/* 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 occured */
+ /* connection has been terminated, or error occurred */
goto done;
}
diff --git a/libpurple/protocols/mxit/login.c b/libpurple/protocols/mxit/login.c
index 9df02bb28b..2cecf0e4b3 100644
--- a/libpurple/protocols/mxit/login.c
+++ b/libpurple/protocols/mxit/login.c
@@ -67,7 +67,7 @@ static struct MXitSession* mxit_create_object( PurpleAccount* account )
/* configure the connection (reference: "libpurple/connection.h") */
con = purple_account_get_connection( account );
con->proto_data = session;
- con->flags |= PURPLE_CONNECTION_NO_BGCOLOR | PURPLE_CONNECTION_NO_URLDESC | PURPLE_CONNECTION_HTML;
+ con->flags |= PURPLE_CONNECTION_NO_BGCOLOR | PURPLE_CONNECTION_NO_URLDESC | PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_SUPPORT_MOODS;
session->con = con;
/* add account */
@@ -238,7 +238,7 @@ static void mxit_cb_register_ok( PurpleConnection *gc, PurpleRequestFields *fiel
/* nickname */
str = purple_request_fields_get_string( fields, "nickname" );
if ( ( !str ) || ( strlen( str ) < 3 ) ) {
- err = _( "The nick name you entered is invalid." );
+ err = _( "The Display Name you entered is invalid." );
goto out;
}
g_strlcpy( profile->nickname, str, sizeof( profile->nickname ) );
@@ -329,17 +329,19 @@ static void mxit_register_view( struct MXitSession* session )
purple_request_fields_add_group( fields, group );
/* mxit login name */
- field = purple_request_field_string_new( "loginname", _( "MXit Login Name" ), purple_account_get_username( session->acc ), FALSE );
+ 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_group_add_field( group, field );
- /* nick name */
- field = purple_request_field_string_new( "nickname", _( "Nick Name" ), profile->nickname, FALSE );
+ /* nick name (required) */
+ field = purple_request_field_string_new( "nickname", _( "Display Name" ), profile->nickname, FALSE );
+ purple_request_field_set_required( field, TRUE );
purple_request_field_group_add_field( group, field );
- /* birthday */
+ /* birthday (required) */
field = purple_request_field_string_new( "bday", _( "Birthday" ), profile->birthday, FALSE );
purple_request_field_string_set_default_value( field, "YYYY-MM-DD" );
+ purple_request_field_set_required( field, TRUE );
purple_request_field_group_add_field( group, field );
/* gender */
@@ -348,12 +350,14 @@ static void mxit_register_view( struct MXitSession* session )
purple_request_field_choice_add( field, _( "Male" ) ); /* 1 */
purple_request_field_group_add_field( group, field );
- /* pin */
+ /* pin (required) */
field = purple_request_field_string_new( "pin", _( "PIN" ), profile->pin, FALSE );
purple_request_field_string_set_masked( field, TRUE );
+ purple_request_field_set_required( field, TRUE );
purple_request_field_group_add_field( group, field );
field = purple_request_field_string_new( "pin2", _( "Verify PIN" ), "", FALSE );
purple_request_field_string_set_masked( field, TRUE );
+ purple_request_field_set_required( field, TRUE );
purple_request_field_group_add_field( group, field );
/* show the form to the user to complete */
@@ -414,10 +418,10 @@ static void mxit_cb_clientinfo2( PurpleUtilFetchUrlData* url_data, gpointer user
purple_connection_error( session->con, _( "Invalid country selected. Please try again." ) );
return;
case '6' :
- purple_connection_error( session->con, _( "Username is not registered. Please register first." ) );
+ purple_connection_error( session->con, _( "The MXit ID you entered is not registered. Please register first." ) );
return;
case '7' :
- purple_connection_error( session->con, _( "Username is already registered. Please choose another username." ) );
+ purple_connection_error( session->con, _( "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;
@@ -633,11 +637,12 @@ static void mxit_cb_clientinfo1( PurpleUtilFetchUrlData* url_data, gpointer user
/* add the captcha */
logindata->captcha = purple_base64_decode( parts[3], &logindata->captcha_size );
- field = purple_request_field_image_new( "capcha", _( "Security Code" ), (gchar*) logindata->captcha, logindata->captcha_size );
+ field = purple_request_field_image_new( "captcha", _( "Security Code" ), (gchar*) logindata->captcha, logindata->captcha_size );
purple_request_field_group_add_field( group, field );
- /* ask for input */
+ /* ask for input (required) */
field = purple_request_field_string_new( "code", _( "Enter Security Code" ), NULL, FALSE );
+ purple_request_field_set_required( field, TRUE );
purple_request_field_group_add_field( group, field );
/* choose your country, but be careful, we already know your IP! ;-) */
@@ -654,7 +659,7 @@ static void mxit_cb_clientinfo1( PurpleUtilFetchUrlData* url_data, gpointer user
}
purple_request_field_list_add( field, country[1], 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 */
+ /* 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] );
}
g_strfreev( country );
diff --git a/libpurple/protocols/mxit/markup.c b/libpurple/protocols/mxit/markup.c
index 5fd8eacd2e..3bafe712ec 100644
--- a/libpurple/protocols/mxit/markup.c
+++ b/libpurple/protocols/mxit/markup.c
@@ -257,7 +257,7 @@ static void mxit_show_split_message( struct RXMsgData* mx )
* all the text as is to the conversation window. this message dump is very
* confusing and makes it totally unusable. to work around this we will count
* the amount of tags and if its more than the pidgin threshold, we will just
- * break the message up into smaller parts and send them seperately to pidgin.
+ * break the message up into smaller parts and send them separately to pidgin.
* to the user it will look like multiple messages, but at least he will be able
* to use and understand it.
*/
@@ -662,7 +662,7 @@ static int mxit_parse_vibe( struct RXMsgData* mx, const char* message )
* @param message The message text
* @return The length of the message to skip
*/
-static int mxit_extract_chatroom_nick( struct RXMsgData* mx, char* message, int len )
+static int mxit_extract_chatroom_nick( struct RXMsgData* mx, char* message, int len, int msgflags )
{
int i;
@@ -673,7 +673,6 @@ static int mxit_extract_chatroom_nick( struct RXMsgData* mx, char* message, int
* Search for it....
*/
gboolean found = FALSE;
- gchar* nickname;
for ( i = 1; i < len; i++ ) {
if ( ( message[i] == '\n' ) && ( message[i-1] == '>' ) ) {
@@ -685,12 +684,30 @@ static int mxit_extract_chatroom_nick( struct RXMsgData* mx, char* message, int
}
if ( found ) {
+ gchar* nickname;
+
/*
* The message definitely had an embedded nickname - generate a marked-up
* message to be displayed.
*/
nickname = g_markup_escape_text( &message[1], -1 );
+ /* Remove any MXit escaping from nickname ("\X" --> "X") */
+ if ( msgflags & CP_MSG_MARKUP ) {
+ int nicklen = strlen( nickname );
+ int j, k;
+
+ for ( j = 0, k = 0; j < nicklen; j++ ) {
+ if ( nickname[j] == '\\' )
+ j++;
+
+ nickname[k] = nickname[j];
+ k++;
+ }
+
+ nickname[k] = '\0'; /* terminate string */
+ }
+
/* add nickname within some BOLD markup to the new converted message */
g_string_append_printf( mx->msg, "<b>%s:</b> ", nickname );
@@ -747,7 +764,7 @@ void mxit_parse_markup( struct RXMsgData* mx, char* message, int len, short msgt
if ( is_mxit_chatroom_contact( mx->session, mx->from ) ) {
/* chatroom message, so we need to extract and skip the sender's nickname
* which is embedded inside the message */
- i = mxit_extract_chatroom_nick( mx, message, len );
+ i = mxit_extract_chatroom_nick( mx, message, len, msgflags );
}
/* run through the message and check for custom emoticons and markup */
diff --git a/libpurple/protocols/mxit/multimx.c b/libpurple/protocols/mxit/multimx.c
index 683bbca07c..fb471e585e 100644
--- a/libpurple/protocols/mxit/multimx.c
+++ b/libpurple/protocols/mxit/multimx.c
@@ -142,6 +142,10 @@ static struct multimx* room_create(struct MXitSession* session, const char* room
multimx->chatid = groupchatID++;
multimx->state = state;
+ /* determine our nickname (from profile) */
+ if (session->profile && (session->profile->nickname[0] != '\0'))
+ multimx->nickname = g_strdup(session->profile->nickname);
+
/* Add to GroupChat list */
session->rooms = g_list_append(session->rooms, multimx);
@@ -160,6 +164,10 @@ static void room_remove(struct MXitSession* session, struct multimx* multimx)
/* Remove from GroupChat list */
session->rooms = g_list_remove(session->rooms, multimx);
+ /* free nickname */
+ if (multimx->nickname)
+ g_free(multimx->nickname);
+
/* Deallocate it */
free (multimx);
multimx = NULL;
@@ -213,6 +221,38 @@ static void member_removed(struct MXitSession* session, struct multimx* multimx,
/*------------------------------------------------------------------------
+ * A user was kicked from the GroupChat.
+ *
+ * @param session The MXit session object
+ * @param multimx The MultiMX room object
+ * @param nickname The nickname of the user who was kicked
+ */
+static void member_kicked(struct MXitSession* session, struct multimx* multimx, const char* nickname)
+{
+ PurpleConversation *convo;
+
+ purple_debug_info(MXIT_PLUGIN_ID, "member_kicked: '%s'\n", nickname);
+
+ convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, multimx->roomname, session->acc);
+ if (convo == NULL) {
+ purple_debug_error(MXIT_PLUGIN_ID, "Conversation '%s' not found\n", multimx->roomname);
+ return;
+ }
+
+ /* who was kicked? - compare to our original nickname */
+ if (purple_utf8_strcasecmp(nickname, multimx->nickname) == 0)
+ {
+ /* you were kicked */
+ 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);
+ }
+ else
+ purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo), nickname, _("was kicked"));
+}
+
+
+/*------------------------------------------------------------------------
* Update the full GroupChat member list.
*
* @param session The MXit session object
@@ -375,6 +415,12 @@ void multimx_message_received(struct RXMsgData* mx, char* msg, int msglen, short
member_removed(mx->session, multimx, msg);
mx->processed = TRUE;
}
+ else if ((ofs = strstr(msg, " has been kicked")) != NULL) {
+ /* Somebody has been kicked */
+ *ofs = '\0';
+ member_kicked(mx->session, multimx, msg);
+ mx->processed = TRUE;
+ }
else if (g_str_has_prefix(msg, "The following users are in this MultiMx:") == TRUE) {
member_update(mx->session, multimx, msg + strlen("The following users are in this MultiMx:") + 1);
mx->processed = TRUE;
@@ -511,6 +557,9 @@ void mxit_chat_invite(PurpleConnection *gc, int id, const char *msg, const char
{
struct MXitSession* session = (struct MXitSession*) gc->proto_data;
struct multimx* multimx = NULL;
+ PurpleBuddy* buddy;
+ PurpleConversation *convo;
+ char* tmp;
purple_debug_info(MXIT_PLUGIN_ID, "Groupchat invite to '%s'\n", username);
@@ -523,6 +572,24 @@ 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);
+ 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) {
+ 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));
+ g_free(tmp);
}
@@ -582,8 +649,8 @@ int mxit_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMess
mxit_send_message(session, multimx->roomid, message, TRUE, FALSE);
/* Determine our nickname to display */
- if (session->profile && (session->profile->nickname[0] != '\0')) /* default is profile name (since that's what everybody else sees) */
- nickname = session->profile->nickname;
+ if (multimx->nickname)
+ nickname = multimx->nickname;
else
nickname = purple_account_get_alias(purple_connection_get_account(gc)); /* local alias */
diff --git a/libpurple/protocols/mxit/multimx.h b/libpurple/protocols/mxit/multimx.h
index 868f91b216..e9470b6665 100644
--- a/libpurple/protocols/mxit/multimx.h
+++ b/libpurple/protocols/mxit/multimx.h
@@ -41,6 +41,7 @@ struct multimx {
char roomname[MXIT_CP_MAX_ALIAS_LEN]; /* name of the room */
char roomid[MXIT_CP_MAX_JID_LEN]; /* internal JID for room */
int chatid; /* libpurple chat ID */
+ char* nickname; /* our nickname in the room */
short state; /* state */
};
diff --git a/libpurple/protocols/mxit/mxit.c b/libpurple/protocols/mxit/mxit.c
index ee8eb8a9d2..2964cfc293 100644
--- a/libpurple/protocols/mxit/mxit.c
+++ b/libpurple/protocols/mxit/mxit.c
@@ -91,6 +91,11 @@ static void* mxit_link_click( const char* link64 )
if ( !account )
goto skip;
con = purple_account_get_connection( account );
+ if ( !con )
+ goto skip;
+
+ /* determine if it's a command-response to send */
+ is_command = g_str_has_prefix( parts[4], "::type=reply|" );
/* send click message back to MXit */
mxit_send_message( con->proto_data, parts[3], parts[4], FALSE, is_command );
@@ -142,7 +147,7 @@ void mxit_register_uri_handler(void)
/*------------------------------------------------------------------------
- * Unegister MXit from receiving URI click notifications from the UI
+ * Unregister MXit from receiving URI click notifications from the UI
*/
static void mxit_unregister_uri_handler()
{
@@ -200,7 +205,7 @@ static void mxit_cb_chat_created( PurpleConversation* conv, struct MXitSession*
if ( find_active_chat( session->active_chats, who ) )
return;
- /* determite if this buddy is a MXit service */
+ /* determine if this buddy is a MXit service */
switch ( contact->type ) {
case MXIT_TYPE_BOT :
case MXIT_TYPE_CHATROOM :
@@ -349,6 +354,10 @@ static void mxit_tooltip( PurpleBuddy* buddy, PurpleNotifyUserInfo* info, gboole
if ( contact->subtype != 0 )
purple_notify_user_info_add_pair( 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 );
+
/* hidden number */
if ( contact->flags & MXIT_CFLAG_HIDDEN )
purple_notify_user_info_add_pair( info, _( "Hidden Number" ), _( "Yes" ) );
@@ -418,6 +427,24 @@ static void mxit_set_status( PurpleAccount* account, PurpleStatus* status )
char* statusmsg1;
char* statusmsg2;
+ /* Handle mood changes */
+ if (purple_status_type_get_primitive(purple_status_get_type(status)) == PURPLE_STATUS_MOOD) {
+ const char* moodid = purple_status_get_attr_string( status, PURPLE_MOOD_NAME );
+ int mood;
+
+ /* convert the purple mood to a mxit mood */
+ mood = mxit_convert_mood( moodid );
+ if ( mood < 0 ) {
+ /* error, mood not found */
+ purple_debug_info( MXIT_PLUGIN_ID, "Mood status NOT found! (id = %s)\n", moodid );
+ return;
+ }
+
+ /* update mood state */
+ mxit_send_mood( session, mood );
+ return;
+ }
+
/* get the status id (reference: "libpurple/status.h") */
statusid = purple_status_get_id( status );
@@ -470,6 +497,8 @@ static void mxit_free_buddy( PurpleBuddy* buddy )
g_free( contact->statusMsg );
if ( contact->avatarId )
g_free( contact->avatarId );
+ if ( contact->msg )
+ g_free( contact->msg );
g_free( contact );
}
@@ -531,8 +560,9 @@ static void mxit_set_buddy_icon( PurpleConnection *gc, PurpleStoredImage *img )
static void mxit_get_info( PurpleConnection *gc, const char *who )
{
struct MXitSession* session = (struct MXitSession*) gc->proto_data;
- const char* profilelist[] = { CP_PROFILE_BIRTHDATE, CP_PROFILE_GENDER, CP_PROFILE_HIDENUMBER, CP_PROFILE_FULLNAME,
- CP_PROFILE_TITLE, CP_PROFILE_FIRSTNAME, CP_PROFILE_LASTNAME, CP_PROFILE_EMAIL };
+ const char* profilelist[] = { CP_PROFILE_BIRTHDATE, CP_PROFILE_GENDER, CP_PROFILE_FULLNAME,
+ CP_PROFILE_FIRSTNAME, CP_PROFILE_LASTNAME, CP_PROFILE_REGCOUNTRY, CP_PROFILE_LASTSEEN,
+ CP_PROFILE_STATUS, CP_PROFILE_AVATAR };
purple_debug_info( MXIT_PLUGIN_ID, "mxit_get_info: '%s'\n", who );
@@ -551,11 +581,34 @@ static GHashTable* mxit_get_text_table( PurpleAccount* acc )
table = g_hash_table_new( g_str_hash, g_str_equal );
- g_hash_table_insert( table, "login_label", (gpointer)_( "Your Mobile Number..." ) );
+ g_hash_table_insert( table, "login_label", (gpointer)_( "Your MXit ID..." ) );
return table;
}
+
+/*------------------------------------------------------------------------
+ * Buddy list menu.
+ *
+ * @param node The entry in the buddy list.
+ */
+static GList* mxit_blist_menu( PurpleBlistNode *node )
+{
+ PurpleBuddy* buddy;
+ struct contact* contact;
+ GList* m = NULL;
+
+ if ( !PURPLE_BLIST_NODE_IS_BUDDY( node ) )
+ return NULL;
+
+ buddy = (PurpleBuddy *) node;
+ contact = purple_buddy_get_protocol_data( buddy );
+ if ( !contact )
+ return NULL;
+
+ return m;
+}
+
/*========================================================================================================================*/
static PurplePluginProtocolInfo proto_info = {
@@ -567,7 +620,7 @@ static PurplePluginProtocolInfo proto_info = {
32, 32, /* min width & height */
MXIT_AVATAR_SIZE, /* max width */
MXIT_AVATAR_SIZE, /* max height */
- 100000, /* max filezize */
+ 100000, /* max filesize */
PURPLE_ICON_SCALE_SEND | PURPLE_ICON_SCALE_DISPLAY /* scaling rules */
},
mxit_list_icon, /* list_icon */
@@ -575,7 +628,7 @@ static PurplePluginProtocolInfo proto_info = {
mxit_status_text, /* status_text */
mxit_tooltip, /* tooltip_text */
mxit_status_types, /* status types [roster.c] */
- NULL, /* blist_node_menu */
+ mxit_blist_menu, /* blist_node_menu */
mxit_chat_info, /* chat_info [multimx.c] */
NULL, /* chat_info_defaults */
mxit_login, /* login [login.c] */
@@ -635,7 +688,9 @@ static PurplePluginProtocolInfo proto_info = {
mxit_get_text_table, /* get_account_text_table */
NULL, /* initiate_media */
NULL, /* get_media_caps */
- NULL /* get_moods */
+ mxit_get_moods, /* get_moods */
+ NULL, /* set_public_alias */
+ NULL /* get_public_alias */
};
diff --git a/libpurple/protocols/mxit/mxit.h b/libpurple/protocols/mxit/mxit.h
index 21bd16f2a8..915a00bd91 100644
--- a/libpurple/protocols/mxit/mxit.h
+++ b/libpurple/protocols/mxit/mxit.h
@@ -63,7 +63,7 @@
/* Plugin details */
#define MXIT_PLUGIN_ID "prpl-loubserp-mxit"
#define MXIT_PLUGIN_NAME "MXit"
-#define MXIT_PLUGIN_VERSION "2.3.0"
+#define MXIT_PLUGIN_VERSION "2.4.0"
#define MXIT_PLUGIN_EMAIL "Pieter Loubser <libpurple@mxit.com>"
#define MXIT_PLUGIN_WWW "http://www.mxit.com"
#define MXIT_PLUGIN_SUMMARY "MXit Protocol Plugin"
@@ -105,7 +105,7 @@
/* Client session flags */
#define MXIT_FLAG_CONNECTED 0x01 /* established connection to the server */
#define MXIT_FLAG_LOGGEDIN 0x02 /* user currently logged in */
-#define MXIT_FLAG_FIRSTROSTER 0x04 /* set to true once the first roster update has been recevied and processed */
+#define MXIT_FLAG_FIRSTROSTER 0x04 /* set to true once the first roster update has been received and processed */
/* define this to enable the link clicking support */
@@ -151,7 +151,7 @@ struct MXitSession {
/* personal (profile) */
struct MXitProfile* profile; /* user's profile information */
- int mood; /* user's current mood */
+ char* uid; /* the user's UID */
/* libpurple */
PurpleAccount* acc; /* pointer to the libpurple internal account struct */
diff --git a/libpurple/protocols/mxit/profile.c b/libpurple/protocols/mxit/profile.c
index b3ad20dd60..aad1d63b3a 100644
--- a/libpurple/protocols/mxit/profile.c
+++ b/libpurple/protocols/mxit/profile.c
@@ -101,6 +101,23 @@ gboolean validateDate( const char* bday )
/*------------------------------------------------------------------------
+ * Returns timestamp field in date & time format (DD-MM-YYYY HH:MM:SS)
+ *
+ * @param msecs The timestamps (milliseconds since epoch)
+ * @return Date & Time in a display'able format.
+ */
+static const char* datetime( gint64 msecs )
+{
+ time_t secs = msecs / 1000;
+
+ struct tm t;
+ localtime_r( &secs, &t );
+
+ return purple_utf8_strftime( "%d-%m-%Y %H:%M:%S", &t );
+}
+
+
+/*------------------------------------------------------------------------
* Display the profile information.
*
* @param session The MXit session object
@@ -120,18 +137,17 @@ void mxit_show_profile( struct MXitSession* session, const char* username, struc
contact = purple_buddy_get_protocol_data(buddy);
}
- purple_notify_user_info_add_pair( info, _( "Nick Name" ), profile->nickname );
+ purple_notify_user_info_add_pair( info, _( "Display Name" ), profile->nickname );
purple_notify_user_info_add_pair( info, _( "Birthday" ), profile->birthday );
purple_notify_user_info_add_pair( info, _( "Gender" ), profile->male ? _( "Male" ) : _( "Female" ) );
- purple_notify_user_info_add_pair( info, _( "Hidden Number" ), profile->hidden ? _( "Yes" ) : _( "No" ) );
-
- purple_notify_user_info_add_section_break( info );
+// purple_notify_user_info_add_pair( info, _( "Hidden Number" ), profile->hidden ? _( "Yes" ) : _( "No" ) );
/* optional information */
- purple_notify_user_info_add_pair( info, _( "Job Title" ), profile->title );
+// purple_notify_user_info_add_pair( info, _( "Title" ), profile->title );
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, _( "Email" ), profile->email );
+// purple_notify_user_info_add_pair( info, _( "Email" ), profile->email );
+ purple_notify_user_info_add_pair( info, _( "Country" ), profile->regcountry );
purple_notify_user_info_add_section_break( info );
@@ -139,6 +155,10 @@ void mxit_show_profile( struct MXitSession* session, const char* username, struc
/* presence */
purple_notify_user_info_add_pair( 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 ) );
+
/* mood */
if ( contact->mood != MXIT_MOOD_NONE )
purple_notify_user_info_add_pair( info, _( "Mood" ), mxit_convert_mood_to_name( contact->mood ) );
@@ -151,6 +171,9 @@ void mxit_show_profile( struct MXitSession* session, const char* username, struc
/* subscription type */
purple_notify_user_info_add_pair( info, _( "Subscription" ), mxit_convert_subtype_to_name( contact->subtype ) );
+
+ /* hidden number */
+ purple_notify_user_info_add_pair( info, _( "Hidden Number" ), ( contact->flags & MXIT_CFLAG_HIDDEN ) ? _( "Yes" ) : _( "No" ) );
}
purple_notify_userinfo( session->con, username, info, NULL, NULL );
diff --git a/libpurple/protocols/mxit/profile.h b/libpurple/protocols/mxit/profile.h
index 0e36cc7e83..31efe705ca 100644
--- a/libpurple/protocols/mxit/profile.h
+++ b/libpurple/protocols/mxit/profile.h
@@ -43,6 +43,9 @@ struct MXitProfile {
char lastname[64]; /* user's last name (aka 'surname') */
char email[64]; /* user's email address */
char mobilenr[21]; /* user's mobile number */
+ char regcountry[3]; /* user's registered country code */
+ gint64 flags; /* user's profile flags */
+ gint64 lastonline; /* user's last-online timestamp */
gboolean hidden; /* set if the user's msisdn should remain hidden */
};
diff --git a/libpurple/protocols/mxit/protocol.c b/libpurple/protocols/mxit/protocol.c
index 166f54f472..851496dac2 100644
--- a/libpurple/protocols/mxit/protocol.c
+++ b/libpurple/protocols/mxit/protocol.c
@@ -445,7 +445,7 @@ static void mxit_queue_packet( struct MXitSession* session, const char* data, in
packet->headerlen = 0;
/* create generic packet header */
- hlen = sprintf( header, "id=%s%c", session->acc->username, CP_REC_TERM ); /* client msisdn */
+ hlen = snprintf( header, sizeof( header ), "id=%s%c", session->acc->username, CP_REC_TERM ); /* client msisdn */
if ( session->http ) {
/* http connection only */
@@ -520,7 +520,7 @@ gboolean mxit_manage_queue( gpointer user_data )
/* we are still waiting for an outstanding ACK from the MXit server */
if ( session->last_tx <= time( NULL ) - MXIT_ACK_TIMEOUT ) {
/* ack timeout! so we close the connection here */
- purple_debug_info( MXIT_PLUGIN_ID, "mxit_manage_queue: Timeout awaiting ACK for command '%X'\n", session->outack );
+ 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." ) );
}
return TRUE;
@@ -642,7 +642,8 @@ void mxit_send_register( struct MXitSession* session )
locale = purple_account_get_string( session->acc, MXIT_CONFIG_LOCALE, MXIT_DEFAULT_LOCALE );
/* convert the packet to a byte stream */
- datalen = sprintf( data, "ms=%s%c%s%c%i%c%s%c" /* "ms"=password\1version\1maxreplyLen\1name\1 */
+ datalen = snprintf( data, sizeof( data ),
+ "ms=%s%c%s%c%i%c%s%c" /* "ms"=password\1version\1maxreplyLen\1name\1 */
"%s%c%i%c%s%c%s%c" /* dateOfBirth\1gender\1location\1capabilities\1 */
"%s%c%i%c%s%c%s", /* dc\1features\1dialingcode\1locale */
session->encpwd, CP_FLD_TERM, MXIT_CP_VERSION, CP_FLD_TERM, CP_MAX_FILESIZE, CP_FLD_TERM, profile->nickname, CP_FLD_TERM,
@@ -670,12 +671,15 @@ void mxit_send_login( struct MXitSession* session )
locale = purple_account_get_string( session->acc, MXIT_CONFIG_LOCALE, MXIT_DEFAULT_LOCALE );
/* convert the packet to a byte stream */
- datalen = sprintf( data, "ms=%s%c%s%c%i%c" /* "ms"=password\1version\1getContacts\1 */
+ datalen = snprintf( data, sizeof( data ),
+ "ms=%s%c%s%c%i%c" /* "ms"=password\1version\1getContacts\1 */
"%s%c%s%c%i%c" /* capabilities\1dc\1features\1 */
- "%s%c%s", /* dialingcode\1locale */
+ "%s%c%s%c" /* dialingcode\1locale\1 */
+ "%i%c%i%c%i", /* maxReplyLen\1protocolVer\1lastRosterUpdate */
session->encpwd, CP_FLD_TERM, MXIT_CP_VERSION, CP_FLD_TERM, 1, CP_FLD_TERM,
MXIT_CP_CAP, CP_FLD_TERM, session->distcode, CP_FLD_TERM, MXIT_CP_FEATURES, CP_FLD_TERM,
- session->dialcode, CP_FLD_TERM, locale
+ session->dialcode, CP_FLD_TERM, locale, CP_FLD_TERM,
+ CP_MAX_FILESIZE, CP_FLD_TERM, MXIT_CP_PROTO_VESION, CP_FLD_TERM, 0
);
/* include "custom resource" information */
@@ -709,7 +713,8 @@ void mxit_send_message( struct MXitSession* session, const char* to, const char*
markuped_msg = g_strdup( msg );
/* convert the packet to a byte stream */
- datalen = sprintf( data, "ms=%s%c%s%c%i%c%i", /* "ms"=jid\1msg\1type\1flags */
+ datalen = snprintf( data, sizeof( data ),
+ "ms=%s%c%s%c%i%c%i", /* "ms"=jid\1msg\1type\1flags */
to, CP_FLD_TERM, markuped_msg, CP_FLD_TERM, msgtype, CP_FLD_TERM, CP_MSG_MARKUP | CP_MSG_EMOTICON
);
@@ -735,7 +740,8 @@ void mxit_send_extprofile_request( struct MXitSession* session, const char* user
int datalen;
unsigned int i;
- datalen = sprintf( data, "ms=%s%c%i", /* "ms="mxitid\1nr_attributes */
+ datalen = snprintf( data, sizeof( data ),
+ "ms=%s%c%i", /* "ms="mxitid\1nr_attributes */
(username ? username : ""), CP_FLD_TERM, nr_attrib);
/* add attributes */
@@ -765,7 +771,8 @@ void mxit_send_extprofile_update( struct MXitSession* session, const char* passw
parts = g_strsplit( attributes, "\01", ( MXIT_MAX_ATTRIBS * 3 ) );
/* convert the packet to a byte stream */
- datalen = sprintf( data, "ms=%s%c%i", /* "ms"=password\1nr_attibutes */
+ datalen = snprintf( data, sizeof( data ),
+ "ms=%s%c%i", /* "ms"=password\1nr_attibutes */
( password ) ? password : "", CP_FLD_TERM, nr_attrib
);
@@ -795,7 +802,8 @@ void mxit_send_presence( struct MXitSession* session, int presence, const char*
int datalen;
/* convert the packet to a byte stream */
- datalen = sprintf( data, "ms=%i%c", /* "ms"=show\1status */
+ datalen = snprintf( data, sizeof( data ),
+ "ms=%i%c", /* "ms"=show\1status */
presence, CP_FLD_TERM
);
@@ -820,7 +828,8 @@ void mxit_send_mood( struct MXitSession* session, int mood )
int datalen;
/* convert the packet to a byte stream */
- datalen = sprintf( data, "ms=%i", /* "ms"=mood */
+ datalen = snprintf( data, sizeof( data ),
+ "ms=%i", /* "ms"=mood */
mood
);
@@ -843,7 +852,8 @@ void mxit_send_invite( struct MXitSession* session, const char* username, const
int datalen;
/* convert the packet to a byte stream */
- datalen = sprintf( data, "ms=%s%c%s%c%s%c%i%c%s", /* "ms"=group\1username\1alias\1type\1msg */
+ datalen = snprintf( data, sizeof( data ),
+ "ms=%s%c%s%c%s%c%i%c%s", /* "ms"=group\1username\1alias\1type\1msg */
groupname, CP_FLD_TERM, username, CP_FLD_TERM, alias,
CP_FLD_TERM, MXIT_TYPE_MXIT, CP_FLD_TERM, ""
);
@@ -865,7 +875,8 @@ void mxit_send_remove( struct MXitSession* session, const char* username )
int datalen;
/* convert the packet to a byte stream */
- datalen = sprintf( data, "ms=%s", /* "ms"=username */
+ datalen = snprintf( data, sizeof( data ),
+ "ms=%s", /* "ms"=username */
username
);
@@ -887,7 +898,8 @@ void mxit_send_allow_sub( struct MXitSession* session, const char* username, con
int datalen;
/* convert the packet to a byte stream */
- datalen = sprintf( data, "ms=%s%c%s%c%s", /* "ms"=username\1group\1alias */
+ datalen = snprintf( data, sizeof( data ),
+ "ms=%s%c%s%c%s", /* "ms"=username\1group\1alias */
username, CP_FLD_TERM, "", CP_FLD_TERM, alias
);
@@ -908,7 +920,8 @@ void mxit_send_deny_sub( struct MXitSession* session, const char* username )
int datalen;
/* convert the packet to a byte stream */
- datalen = sprintf( data, "ms=%s", /* "ms"=username */
+ datalen = snprintf( data, sizeof( data ),
+ "ms=%s", /* "ms"=username */
username
);
@@ -931,7 +944,8 @@ void mxit_send_update_contact( struct MXitSession* session, const char* username
int datalen;
/* convert the packet to a byte stream */
- datalen = sprintf( data, "ms=%s%c%s%c%s", /* "ms"=groupname\1username\1alias */
+ datalen = snprintf( data, sizeof( data ),
+ "ms=%s%c%s%c%s", /* "ms"=groupname\1username\1alias */
groupname, CP_FLD_TERM, username, CP_FLD_TERM, alias
);
@@ -952,7 +966,8 @@ void mxit_send_splashclick( struct MXitSession* session, const char* splashid )
int datalen;
/* convert the packet to a byte stream */
- datalen = sprintf( data, "ms=%s", /* "ms"=splashId */
+ datalen = snprintf( data, sizeof( data ),
+ "ms=%s", /* "ms"=splashId */
splashid
);
@@ -962,6 +977,32 @@ void mxit_send_splashclick( struct MXitSession* session, const char* splashid )
/*------------------------------------------------------------------------
+ * Send a message event packet.
+ *
+ * @param session The MXit session object
+ * @param to The username of the original sender (ie, recipient of the event)
+ * @param id The identifier of the event (received in message)
+ * @param event Identified the type of event
+ */
+void mxit_send_msgevent( struct MXitSession* session, const char* to, const char* id, int event)
+{
+ char data[CP_MAX_PACKET];
+ int datalen;
+
+ purple_debug_info( MXIT_PLUGIN_ID, "mxit_send_msgevent: to=%s id=%s event=%i\n", to, id, event );
+
+ /* convert the packet to a byte stream */
+ datalen = snprintf( data, sizeof( data ),
+ "ms=%s%c%s%c%i", /* "ms"=contactAddress \1 id \1 event */
+ to, CP_FLD_TERM, id, CP_FLD_TERM, event
+ );
+
+ /* queue packet for transmission */
+ mxit_queue_packet( session, data, datalen, CP_CMD_MSGEVENT );
+}
+
+
+/*------------------------------------------------------------------------
* Send packet to create a MultiMX room.
*
* @param session The MXit session object
@@ -976,7 +1017,8 @@ void mxit_send_groupchat_create( struct MXitSession* session, const char* groupn
int i;
/* convert the packet to a byte stream */
- datalen = sprintf( data, "ms=%s%c%i", /* "ms"=roomname\1nr_jids\1jid0\1..\1jidN */
+ datalen = snprintf( data, sizeof( data ),
+ "ms=%s%c%i", /* "ms"=roomname\1nr_jids\1jid0\1..\1jidN */
groupname, CP_FLD_TERM, nr_usernames
);
@@ -1005,7 +1047,8 @@ void mxit_send_groupchat_invite( struct MXitSession* session, const char* roomid
int i;
/* convert the packet to a byte stream */
- datalen = sprintf( data, "ms=%s%c%i", /* "ms"=roomid\1nr_jids\1jid0\1..\1jidN */
+ datalen = snprintf( data, sizeof( data ),
+ "ms=%s%c%i", /* "ms"=roomid\1nr_jids\1jid0\1..\1jidN */
roomid, CP_FLD_TERM, nr_usernames
);
@@ -1158,7 +1201,7 @@ void mxit_send_file_received( struct MXitSession* session, const char* fileid, s
return;
}
- set_chunk_type( chunk, CP_CHUNK_RECIEVED );
+ set_chunk_type( chunk, CP_CHUNK_RECEIVED );
set_chunk_length( chunk, size );
datalen += MXIT_CHUNK_HEADER_SIZE + size;
@@ -1257,7 +1300,7 @@ static void mxit_parse_cmd_login( struct MXitSession* session, struct record** r
const char* statusmsg;
const char* profilelist[] = { CP_PROFILE_BIRTHDATE, CP_PROFILE_GENDER, CP_PROFILE_HIDENUMBER, CP_PROFILE_FULLNAME,
CP_PROFILE_TITLE, CP_PROFILE_FIRSTNAME, CP_PROFILE_LASTNAME, CP_PROFILE_EMAIL,
- CP_PROFILE_MOBILENR };
+ CP_PROFILE_MOBILENR, CP_PROFILE_FLAGS };
purple_account_set_int( session->acc, MXIT_CONFIG_STATE, MXIT_STATE_LOGIN );
@@ -1266,6 +1309,19 @@ static void mxit_parse_cmd_login( struct MXitSession* session, struct record** r
purple_connection_update_progress( session->con, _( "Successfully Logged In..." ), 3, 4 );
purple_connection_set_state( session->con, PURPLE_CONNECTED );
+ /* save extra info if this is a HTTP connection */
+ if ( session->http ) {
+ /* save the http server to use for this session */
+ g_strlcpy( session->http_server, records[1]->fields[3]->data, sizeof( session->http_server ) );
+
+ /* save the session id */
+ session->http_sesid = atoi( records[0]->fields[0]->data );
+ }
+
+ /* extract MXitId (from protocol 5.9) */
+ if ( records[1]->fcount >= 9 )
+ session->uid = g_strdup( records[1]->fields[8]->data );
+
/* display the current splash-screen */
if ( splash_popup_enabled( session ) )
splash_display( session );
@@ -1290,15 +1346,6 @@ static void mxit_parse_cmd_login( struct MXitSession* session, struct record** r
g_free( statusmsg2 );
}
- /* save extra info if this is a HTTP connection */
- if ( session->http ) {
- /* save the http server to use for this session */
- g_strlcpy( session->http_server, records[1]->fields[3]->data, sizeof( session->http_server ) );
-
- /* save the session id */
- session->http_sesid = atoi( records[0]->fields[0]->data );
- }
-
/* retrieve our MXit profile */
mxit_send_extprofile_request( session, NULL, ARRAY_SIZE( profilelist ), profilelist );
}
@@ -1355,6 +1402,12 @@ static void mxit_parse_cmd_message( struct MXitSession* session, struct record**
return;
}
+ if ( msgflags & CP_MSG_NOTIFY_DELIVERY ) {
+ /* delivery notification is requested */
+ if ( records[0]->fcount >= 4 )
+ mxit_send_msgevent( session, records[0]->fields[0]->data, records[0]->fields[3]->data, CP_MSGEVENT_DELIVERED );
+ }
+
/* create and initialise new markup struct */
mx = g_new0( struct RXMsgData, 1 );
mx->msg = g_string_sized_new( msglen );
@@ -1419,9 +1472,9 @@ static void mxit_parse_cmd_new_sub( struct MXitSession* session, struct record**
/* build up a new contact info struct */
contact = g_new0( struct contact, 1 );
- strcpy( contact->username, rec->fields[0]->data );
+ g_strlcpy( contact->username, rec->fields[0]->data, sizeof( contact->username ) );
mxit_strip_domain( contact->username ); /* remove dummy domain */
- strcpy( contact->alias, rec->fields[1]->data );
+ g_strlcpy( contact->alias, rec->fields[1]->data, sizeof( contact->alias ) );
contact->type = atoi( rec->fields[2]->data );
if ( rec->fcount >= 5 ) {
@@ -1472,20 +1525,24 @@ static void mxit_parse_cmd_contact( struct MXitSession* session, struct record**
/* build up a new contact info struct */
contact = g_new0( struct contact, 1 );
- strcpy( contact->groupname, rec->fields[0]->data );
- strcpy( contact->username, rec->fields[1]->data );
+ g_strlcpy( contact->groupname, rec->fields[0]->data, sizeof( contact->groupname ) );
+ g_strlcpy( contact->username, rec->fields[1]->data, sizeof( contact->username ) );
mxit_strip_domain( contact->username ); /* remove dummy domain */
- strcpy( contact->alias, rec->fields[2]->data );
+ g_strlcpy( contact->alias, rec->fields[2]->data, sizeof( contact->alias ) );
contact->presence = atoi( rec->fields[3]->data );
contact->type = atoi( rec->fields[4]->data );
contact->mood = atoi( rec->fields[5]->data );
if ( rec->fcount > 6 ) {
- /* added in protocol 5.9.0 - flags & subtype */
+ /* added in protocol 5.9 - flags & subtype */
contact->flags = atoi( rec->fields[6]->data );
contact->subtype = rec->fields[7]->data[0];
}
+ if ( rec->fcount > 8 ) {
+ /* added in protocol 6.0 - reject message */
+ contact->msg = g_strdup( rec->fields[8]->data );
+ }
/* add the contact to the buddy list */
if ( contact-> type == MXIT_TYPE_MULTIMX ) /* contact is a MultiMX room */
@@ -1525,12 +1582,13 @@ static void mxit_parse_cmd_presence( struct MXitSession* session, struct record*
/*
* The format of the record is:
- * contactAddressN\1presenceN\1\moodN\1customMoodN\1statusMsgN\1avatarIdN
+ * contactAddressN\1presenceN\1moodN\1customMoodN\1statusMsgN\1avatarIdN
*/
mxit_strip_domain( rec->fields[0]->data ); /* contactAddress */
mxit_update_buddy_presence( session, rec->fields[0]->data, atoi( rec->fields[1]->data ), atoi( rec->fields[2]->data ),
- rec->fields[3]->data, rec->fields[4]->data, rec->fields[5]->data );
+ rec->fields[3]->data, rec->fields[4]->data );
+ mxit_update_buddy_avatar( session, rec->fields[0]->data, rec->fields[5]->data );
}
}
@@ -1548,10 +1606,21 @@ static void mxit_parse_cmd_extprofile( struct MXitSession* session, struct recor
struct MXitProfile* profile = NULL;
int count;
int i;
+ const char* avatarId = NULL;
+ const char* statusMsg = NULL;
purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_extprofile: profile for '%s'\n", mxitId );
- profile = g_new0( struct MXitProfile, 1 );
+ if ( ( records[0]->fields[0]->len == 0 ) || ( session->uid && ( strcmp( session->uid, records[0]->fields[0]->data ) == 0 ) ) ) {
+ /* No UserId or Our UserId provided, so this must be our own profile information */
+ if ( session->profile == NULL )
+ session->profile = g_new0( struct MXitProfile, 1 );
+ profile = session->profile;
+ }
+ else {
+ /* is a buddy's profile */
+ profile = g_new0( struct MXitProfile, 1 );
+ }
/* set the count for attributes */
count = atoi( records[0]->fields[1]->data );
@@ -1593,8 +1662,13 @@ static void mxit_parse_cmd_extprofile( struct MXitSession* session, struct recor
/* nickname */
g_strlcpy( profile->nickname, fvalue, sizeof( profile->nickname ) );
}
+ else if ( strcmp( CP_PROFILE_STATUS, fname ) == 0 ) {
+ /* status message - just keep a reference to the value */
+ statusMsg = fvalue;
+ }
else if ( strcmp( CP_PROFILE_AVATAR, fname ) == 0 ) {
- /* avatar id, we just ingore it cause we dont need it */
+ /* avatar id - just keep a reference to the value */
+ avatarId = fvalue;
}
else if ( strcmp( CP_PROFILE_TITLE, fname ) == 0 ) {
/* title */
@@ -1616,23 +1690,31 @@ static void mxit_parse_cmd_extprofile( struct MXitSession* session, struct recor
/* mobile number */
g_strlcpy( profile->mobilenr, fvalue, sizeof( profile->mobilenr ) );
}
+ else if ( strcmp( CP_PROFILE_REGCOUNTRY, fname ) == 0 ) {
+ /* registered country */
+ g_strlcpy( profile->regcountry, fvalue, sizeof( profile->regcountry ) );
+ }
+ else if ( strcmp( CP_PROFILE_FLAGS, fname ) == 0 ) {
+ /* profile flags */
+ profile->flags = strtoll( fvalue, NULL, 10 );
+ }
+ else if ( strcmp( CP_PROFILE_LASTSEEN, fname ) == 0 ) {
+ /* last seen online */
+ profile->lastonline = strtoll( fvalue, NULL, 10 );
+ }
else {
/* invalid profile attribute */
purple_debug_error( MXIT_PLUGIN_ID, "Invalid profile attribute received '%s' \n", fname );
}
}
- if ( records[0]->fields[0]->len == 0 ) {
- /* no MXit id provided, so this must be our own profile information */
- if ( session->profile )
- g_free( session->profile );
- session->profile = profile;
- }
- else {
- /* display other user's profile */
- mxit_show_profile( session, mxitId, profile );
+ if ( profile != session->profile ) {
+ /* update avatar (if necessary) */
+ if ( avatarId )
+ mxit_update_buddy_avatar( session, mxitId, avatarId );
- /* cleanup */
+ /* if this is not our profile, just display it */
+ mxit_show_profile( session, mxitId, profile );
g_free( profile );
}
}
@@ -1752,7 +1834,7 @@ static void mxit_parse_cmd_media( struct MXitSession* session, struct record** r
/* this is a ack for a file send. no action is required */
break;
- case CP_CHUNK_RECIEVED :
+ case CP_CHUNK_RECEIVED :
/* this is a ack for a file received. no action is required */
break;
@@ -1917,6 +1999,8 @@ static int process_success_response( struct MXitSession* session, struct rx_pack
/* profile update */
case CP_CMD_SPLASHCLICK :
/* splash-screen clickthrough */
+ case CP_CMD_MSGEVENT :
+ /* event message */
break;
default :
@@ -1962,12 +2046,12 @@ static int process_error_response( struct MXitSession* session, struct rx_packet
return 0;
}
else {
- sprintf( errmsg, _( "Login error: %s (%i)" ), errdesc, packet->errcode );
+ snprintf( errmsg, sizeof( errmsg ), _( "Login error: %s (%i)" ), errdesc, packet->errcode );
purple_connection_error( session->con, errmsg );
return -1;
}
case CP_CMD_LOGOUT :
- sprintf( errmsg, _( "Logout error: %s (%i)" ), errdesc, packet->errcode );
+ snprintf( errmsg, sizeof( errmsg ), _( "Logout error: %s (%i)" ), errdesc, packet->errcode );
purple_connection_error_reason( session->con, PURPLE_CONNECTION_ERROR_NAME_IN_USE, _( errmsg ) );
return -1;
case CP_CMD_CONTACT :
@@ -2020,6 +2104,7 @@ static int process_error_response( struct MXitSession* session, struct rx_packet
mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Profile Error" ), _( errdesc ) );
break;
case CP_CMD_SPLASHCLICK :
+ case CP_CMD_MSGEVENT :
/* ignore error */
break;
case CP_CMD_PING :
@@ -2438,6 +2523,8 @@ void mxit_close_connection( struct MXitSession* session )
mxit_free_emoticon_cache( session );
/* free allocated memory */
+ if ( session->uid )
+ g_free( session->uid );
g_free( session->encpwd );
session->encpwd = NULL;
diff --git a/libpurple/protocols/mxit/protocol.h b/libpurple/protocols/mxit/protocol.h
index 996a71ed38..22d887d667 100644
--- a/libpurple/protocols/mxit/protocol.h
+++ b/libpurple/protocols/mxit/protocol.h
@@ -72,6 +72,9 @@
#define MXIT_CF_EXT_MARKUP 0x040000
#define MXIT_CF_PLAIN_PWD 0x080000
#define MXIT_CF_NO_GATEWAYS 0x100000
+#define MXIT_CF_NO_AVATARS 0x200000
+#define MXIT_CF_GAMING 0x400000
+#define MXIT_CF_GAMING_UPDATE 0x800000
/* Client features supported by this implementation */
#define MXIT_CP_FEATURES ( MXIT_CF_FILE_TRANSFER | MXIT_CF_FILE_ACCESS | MXIT_CF_AUDIO | MXIT_CF_MARKUP | MXIT_CF_EXT_MARKUP | MXIT_CF_NO_GATEWAYS | MXIT_CF_IMAGES | MXIT_CF_COMMANDS | MXIT_CF_VIBES | MXIT_CF_MIDP2 )
@@ -82,11 +85,12 @@
/* MXit client version */
#define MXIT_CP_DISTCODE "P" /* client distribution code (magic, do not touch!) */
-#define MXIT_CP_RELEASE "5.9.0" /* client protocol release version supported */
+#define MXIT_CP_RELEASE "5.9.0" /* client version */
#define MXIT_CP_ARCH "Y" /* client architecture series (Y not for Yoda but for PC-client) */
#define MXIT_CLIENT_ID "LP" /* client ID as specified by MXit */
#define MXIT_CP_PLATFORM "PURPLE" /* client platform */
#define MXIT_CP_VERSION MXIT_CP_DISTCODE"-"MXIT_CP_RELEASE"-"MXIT_CP_ARCH"-"MXIT_CP_PLATFORM
+#define MXIT_CP_PROTO_VESION 60 /* client protocol version */
/* set operating system name */
#if defined( __APPLE__ )
@@ -126,6 +130,7 @@
#define CP_CMD_MEDIA 0x001B /* (27) get multimedia message */
#define CP_CMD_SPLASHCLICK 0x001F /* (31) splash-screen clickthrough */
#define CP_CMD_STATUS 0x0020 /* (32) set shown presence & status */
+#define CP_CMD_MSGEVENT 0x0023 /* (35) Raise message event */
#define CP_CMD_MOOD 0x0029 /* (41) set mood */
#define CP_CMD_KICK 0x002B /* (43) login kick */
#define CP_CMD_GRPCHAT_CREATE 0x002C /* (44) create new groupchat */
@@ -147,6 +152,8 @@
#define RX_STATE_PROC 0x03 /* process read data */
/* message flags */
+#define CP_MSG_NOTIFY_DELIVERY 0x0002 /* request delivery notification */
+#define CP_MSG_NOTIFY_READ 0x0004 /* request read notification */
#define CP_MSG_ENCRYPTED 0x0010 /* message is encrypted */
#define CP_MSG_MARKUP 0x0200 /* message may contain markup */
#define CP_MSG_EMOTICON 0x0400 /* message may contain custom emoticons */
@@ -164,6 +171,9 @@
#define CP_MSGTYPE_FORM 0x06 /* mxit custom form */
#define CP_MSGTYPE_COMMAND 0x07 /* mxit command */
+/* message event types */
+#define CP_MSGEVENT_DELIVERED 0x02 /* message was delivered */
+#define CP_MSGEVENT_DISPLAYED 0x04 /* message was viewed */
/* extended profile attribute fields */
#define CP_PROFILE_BIRTHDATE "birthdate" /* Birthdate (String - ISO 8601 format) */
@@ -179,13 +189,18 @@
#define CP_PROFILE_LASTNAME "lastname" /* Last name (UTF8 String) */
#define CP_PROFILE_EMAIL "email" /* Email address (UTF8 String) */
#define CP_PROFILE_MOBILENR "mobilenumber" /* Mobile Number (UTF8 String) */
+#define CP_PROFILE_REGCOUNTRY "registeredcountry" /* Registered Country Code (UTF8 String) */
+#define CP_PROFILE_FLAGS "flags" /* Profile flags (Bitset) */
+#define CP_PROFILE_LASTSEEN "lastseen" /* Last-Online timestamp */
/* extended profile field types */
-#define CP_PROF_TYPE_BOOL 0x02 /* boolean profile attribute type */
-#define CP_PROF_TYPE_INT 0x05 /* integer profile attribute type */
-#define CP_PROF_TYPE_UTF8 0x0A /* UTF8 string profile attribute type */
-#define CP_PROF_TYPE_DATE 0x0B /* date-time profile attribute type */
+#define CP_PROFILE_TYPE_BOOL 0x02 /* boolean profile attribute type */
+#define CP_PROFILE_TYPE_INT 0x05 /* integer profile attribute type */
+#define CP_PROFILE_TYPE_UTF8 0x0A /* UTF8 string profile attribute type */
+#define CP_PROFILE_TYPE_DATE 0x0B /* date-time profile attribute type */
+/* profile flags */
+#define CP_PROF_DOBLOCKED 0x40 /* date-of-birth cannot be changed */
/* define this to enable protocol debugging (very verbose logging) */
#define DEBUG_PROTOCOL
@@ -284,6 +299,7 @@ void mxit_send_allow_sub( struct MXitSession* session, const char* username, con
void mxit_send_deny_sub( struct MXitSession* session, const char* username );
void mxit_send_update_contact( struct MXitSession* session, const char* username, const char* alias, const char* groupname );
void mxit_send_splashclick( struct MXitSession* session, const char* splashid );
+void mxit_send_msgevent( struct MXitSession* session, const char* to, const char* id, int event);
void mxit_send_file( struct MXitSession* session, const char* username, const char* filename, const unsigned char* buf, int buflen );
void mxit_send_file_reject( struct MXitSession* session, const char* fileid );
diff --git a/libpurple/protocols/mxit/roster.c b/libpurple/protocols/mxit/roster.c
index 03f88c961f..32277f35ac 100644
--- a/libpurple/protocols/mxit/roster.c
+++ b/libpurple/protocols/mxit/roster.c
@@ -44,12 +44,12 @@ struct contact_invite {
/* statuses (reference: libpurple/status.h) */
static struct status
{
- PurpleStatusPrimitive primative;
+ PurpleStatusPrimitive primitive;
int mxit;
const char* id;
const char* name;
} const mxit_statuses[] = {
- /* primative, no, id, name */
+ /* primitive, no, id, name */
{ PURPLE_STATUS_OFFLINE, MXIT_PRESENCE_OFFLINE, "offline", N_( "Offline" ) }, /* 0 */
{ PURPLE_STATUS_AVAILABLE, MXIT_PRESENCE_ONLINE, "online", N_( "Available" ) }, /* 1 */
{ PURPLE_STATUS_AWAY, MXIT_PRESENCE_AWAY, "away", N_( "Away" ) }, /* 2 */
@@ -74,13 +74,19 @@ GList* mxit_status_types( PurpleAccount* account )
const struct status* status = &mxit_statuses[i];
/* add mxit status (reference: "libpurple/status.h") */
- type = purple_status_type_new_with_attrs( status->primative, status->id, _( status->name ), TRUE, TRUE, FALSE,
+ type = purple_status_type_new_with_attrs( status->primitive, status->id, _( status->name ), TRUE, TRUE, FALSE,
"message", _( "Message" ), purple_value_new( PURPLE_TYPE_STRING ),
NULL );
statuslist = g_list_append( statuslist, type );
}
+ /* 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 ),
+ NULL);
+ statuslist = g_list_append( statuslist, type );
+
return statuslist;
}
@@ -127,6 +133,62 @@ const char* mxit_convert_presence_to_name( short no )
* Moods
*/
+/* moods (reference: libpurple/status.h) */
+static PurpleMood mxit_moods[] = {
+ {"angry", N_("Angry"), NULL},
+ {"excited", N_("Excited"), NULL},
+ {"grumpy", N_("Grumpy"), NULL},
+ {"happy", N_("Happy"), NULL},
+ {"in_love", N_("In love"), NULL},
+ {"invincible", N_("Invincible"), NULL},
+ {"sad", N_("Sad"), NULL},
+ {"hot", N_("Hot"), NULL},
+ {"sick", N_("Sick"), NULL},
+ {"sleepy", N_("Sleepy"), NULL},
+ {"bored", N_("Bored"), NULL},
+ {"cold", N_("Cold"), NULL},
+ {"confused", N_("Confused"), NULL},
+ {"hungry", N_("Hungry"), NULL},
+ {"stressed", N_("Stressed"), NULL},
+ /* Mark the last record. */
+ { NULL, NULL, NULL }
+};
+
+
+/*------------------------------------------------------------------------
+ * Returns the MXit mood code, given the unique mood ID.
+ *
+ * @param id The mood ID
+ * @return The MXit mood code
+ */
+int mxit_convert_mood( const char* id )
+{
+ unsigned int i;
+
+ /* Mood is being unset */
+ if ( id == NULL )
+ return MXIT_MOOD_NONE;
+
+ for ( i = 0; i < ARRAY_SIZE( mxit_moods ) - 1; i++ ) {
+ if ( strcmp( mxit_moods[i].mood, id ) == 0 ) /* mood found! */
+ return i + 1; /* because MXIT_MOOD_NONE is 0 */
+ }
+
+ return -1;
+}
+
+
+/*------------------------------------------------------------------------
+ * Return the list of MXit-supported moods.
+ *
+ * @param account The MXit account object
+ */
+PurpleMood* mxit_get_moods(PurpleAccount *account)
+{
+ return mxit_moods;
+}
+
+
/*------------------------------------------------------------------------
* Returns the MXit mood as a string, given the MXit mood's ID.
*
@@ -156,6 +218,16 @@ const char* mxit_convert_mood_to_name( short id )
return _( "Sick" );
case MXIT_MOOD_SLEEPY :
return _( "Sleepy" );
+ case MXIT_MOOD_BORED :
+ return _( "Bored" );
+ case MXIT_MOOD_COLD :
+ return _( "Cold" );
+ case MXIT_MOOD_CONFUSED :
+ return _( "Confused" );
+ case MXIT_MOOD_HUNGRY :
+ return _( "Hungry" );
+ case MXIT_MOOD_STRESSED :
+ return _( "Stressed" );
case MXIT_MOOD_NONE :
default :
return "";
@@ -235,7 +307,7 @@ static PurpleBuddy* mxit_update_buddy_group( struct MXitSession* session, Purple
* XXX: libPurple does not currently provide an API to change or rename the group name
* for a specific buddy. One option is to remove the buddy from the list and re-adding
* him in the new group, but by doing that makes the buddy go offline and then online
- * again. This is really not ideal and very iretating, but how else then?
+ * again. This is really not ideal and very irritating, but how else then?
*/
/* create new buddy */
@@ -259,6 +331,12 @@ static PurpleBuddy* mxit_update_buddy_group( struct MXitSession* session, Purple
else
purple_prpl_got_user_status( session->acc, newbuddy->name, mxit_statuses[contact->presence].id, NULL );
+ /* update the buddy's mood */
+ if ( contact->mood == MXIT_MOOD_NONE )
+ purple_prpl_got_user_status_deactive( session->acc, newbuddy->name, "mood" );
+ else
+ purple_prpl_got_user_status( session->acc, newbuddy->name, "mood", PURPLE_MOOD_NAME, mxit_moods[contact->mood-1].mood, NULL );
+
/* update avatar */
if ( contact->avatarId ) {
mxit_get_avatar( session, newbuddy->name, contact->avatarId );
@@ -295,7 +373,7 @@ void mxit_update_contact( struct MXitSession* session, struct contact* contact )
* So if this MXit contact isn't in a group, pretend it is.
*/
if ( *contact->groupname == '\0' ) {
- strcpy( contact->groupname, MXIT_DEFAULT_GROUP );
+ g_strlcpy( contact->groupname, MXIT_DEFAULT_GROUP, sizeof( contact->groupname ) );
}
/* find or create a group for this contact */
@@ -346,6 +424,12 @@ void mxit_update_contact( struct MXitSession* session, struct contact* contact )
/* update the buddy's status (reference: "libpurple/prpl.h") */
purple_prpl_got_user_status( session->acc, contact->username, mxit_statuses[contact->presence].id, NULL );
+
+ /* update the buddy's mood */
+ if ( contact->mood == MXIT_MOOD_NONE )
+ purple_prpl_got_user_status_deactive( session->acc, contact->username, "mood" );
+ else
+ purple_prpl_got_user_status( session->acc, contact->username, "mood", PURPLE_MOOD_NAME, mxit_moods[contact->mood-1].mood, NULL );
}
@@ -359,15 +443,14 @@ void mxit_update_contact( struct MXitSession* session, struct contact* contact )
* @param mood The new mood for the contact
* @param customMood The custom mood identifier
* @param statusMsg This is the contact's status message
- * @param avatarId This is the contact's avatar id
*/
-void mxit_update_buddy_presence( struct MXitSession* session, const char* username, short presence, short mood, const char* customMood, const char* statusMsg, const char* avatarId )
+void mxit_update_buddy_presence( struct MXitSession* session, const char* username, short presence, short mood, const char* customMood, const char* statusMsg )
{
PurpleBuddy* buddy = NULL;
struct contact* contact = NULL;
- purple_debug_info( MXIT_PLUGIN_ID, "mxit_update_buddy_presence: user='%s' presence=%i mood=%i customMood='%s' statusMsg='%s' avatar='%s'\n",
- username, presence, mood, customMood, statusMsg, avatarId );
+ purple_debug_info( MXIT_PLUGIN_ID, "mxit_update_buddy_presence: user='%s' presence=%i mood=%i customMood='%s' statusMsg='%s'\n",
+ username, presence, mood, customMood, statusMsg );
if ( ( presence < MXIT_PRESENCE_OFFLINE ) || ( presence > MXIT_PRESENCE_DND ) ) {
purple_debug_info( MXIT_PLUGIN_ID, "mxit_update_buddy_presence: invalid presence state %i\n", presence );
@@ -388,6 +471,10 @@ void mxit_update_buddy_presence( struct MXitSession* session, const char* userna
contact->presence = presence;
contact->mood = mood;
+ /* validate mood */
+ if (( contact->mood < MXIT_MOOD_NONE ) || ( contact->mood > MXIT_MOOD_STRESSED ))
+ contact->mood = MXIT_MOOD_NONE;
+
g_strlcpy( contact->customMood, customMood, sizeof( contact->customMood ) );
// TODO: Download custom mood frame.
@@ -399,7 +486,46 @@ void mxit_update_buddy_presence( struct MXitSession* session, const char* userna
if ( statusMsg[0] != '\0' )
contact->statusMsg = g_markup_escape_text( statusMsg, -1 );
- /* update avatarId */
+ /* update the buddy's status (reference: "libpurple/prpl.h") */
+ if ( contact->statusMsg )
+ purple_prpl_got_user_status( session->acc, username, mxit_statuses[contact->presence].id, "message", contact->statusMsg, NULL );
+ else
+ purple_prpl_got_user_status( session->acc, username, mxit_statuses[contact->presence].id, NULL );
+
+ /* update the buddy's mood */
+ if ( contact->mood == MXIT_MOOD_NONE )
+ purple_prpl_got_user_status_deactive( session->acc, username, "mood" );
+ else
+ purple_prpl_got_user_status( session->acc, username, "mood", PURPLE_MOOD_NAME, mxit_moods[contact->mood-1].mood, NULL );
+}
+
+
+/*------------------------------------------------------------------------
+ * Update the buddy's avatar.
+ * Either a presence update packet was received from the MXit server, or a profile response.
+ *
+ * @param session The MXit session object
+ * @param username The contact which presence to update
+ * @param avatarId This is the contact's avatar id
+ */
+void mxit_update_buddy_avatar( struct MXitSession* session, const char* username, const char* avatarId )
+{
+ PurpleBuddy* buddy = NULL;
+ struct contact* contact = NULL;
+
+ 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 );
+ if ( !buddy ) {
+ purple_debug_warning( MXIT_PLUGIN_ID, "mxit_update_buddy_presence: unable to find the buddy '%s'\n", username );
+ return;
+ }
+
+ contact = purple_buddy_get_protocol_data( buddy );
+ if ( !contact )
+ return;
+
if ( ( contact->avatarId ) && ( g_ascii_strcasecmp( contact->avatarId, avatarId ) == 0 ) ) {
/* avatar has not changed - do nothing */
}
@@ -413,12 +539,6 @@ void mxit_update_buddy_presence( struct MXitSession* session, const char* userna
}
else /* clear current avatar */
purple_buddy_icons_set_for_user( session->acc, username, NULL, 0, NULL );
-
- /* update the buddy's status (reference: "libpurple/prpl.h") */
- if ( contact->statusMsg )
- purple_prpl_got_user_status( session->acc, username, mxit_statuses[contact->presence].id, "message", contact->statusMsg, NULL );
- else
- purple_prpl_got_user_status( session->acc, username, mxit_statuses[contact->presence].id, NULL );
}
diff --git a/libpurple/protocols/mxit/roster.h b/libpurple/protocols/mxit/roster.h
index 27c022a280..4bbe9d328c 100644
--- a/libpurple/protocols/mxit/roster.h
+++ b/libpurple/protocols/mxit/roster.h
@@ -66,6 +66,11 @@
#define MXIT_MOOD_HOT 0x08
#define MXIT_MOOD_SICK 0x09
#define MXIT_MOOD_SLEEPY 0x0A
+#define MXIT_MOOD_BORED 0x0B
+#define MXIT_MOOD_COLD 0x0C
+#define MXIT_MOOD_CONFUSED 0x0D
+#define MXIT_MOOD_HUNGRY 0x0E
+#define MXIT_MOOD_STRESSED 0x0F
/* MXit contact flags */
@@ -105,7 +110,7 @@ struct contact {
short presence; /* presence state */
short subtype; /* subscription type */
- char* msg; /* invite message */
+ char* msg; /* invite/rejection message */
char customMood[16]; /* custom mood */
char* statusMsg; /* status message */
@@ -119,11 +124,13 @@ const char* mxit_convert_presence_to_name( short no );
const char* mxit_convert_subtype_to_name( short subtype );
/* Moods */
+int mxit_convert_mood( const char* id );
const char* mxit_convert_mood_to_name( short id );
/* MXit Protocol callbacks */
void mxit_update_contact( struct MXitSession* session, struct contact* contact );
-void mxit_update_buddy_presence( struct MXitSession* session, const char* username, short presence, short mood, const char* customMood, const char* statusMsg, const char* avatarId );
+void mxit_update_buddy_presence( struct MXitSession* session, const char* username, short presence, short mood, const char* customMood, const char* statusMsg );
+void mxit_update_buddy_avatar( struct MXitSession* session, const char* username, const char* avatarId );
void mxit_new_subscription( struct MXitSession* session, struct contact* contact );
void mxit_update_blist( struct MXitSession* session );
gboolean is_mxit_chatroom_contact( struct MXitSession* session, const char* username );
@@ -134,6 +141,7 @@ void mxit_remove_buddy( PurpleConnection* gc, PurpleBuddy* buddy, PurpleGroup* g
void mxit_buddy_alias( PurpleConnection* gc, const char* who, const char* alias );
void mxit_buddy_group( PurpleConnection* gc, const char* who, const char* old_group, const char* new_group );
void mxit_rename_group( PurpleConnection* gc, const char* old_name, PurpleGroup* group, GList* moved_buddies );
+PurpleMood* mxit_get_moods(PurpleAccount *account);
#endif /* _MXIT_ROSTER_H_ */
diff --git a/libpurple/protocols/myspace/myspace.c b/libpurple/protocols/myspace/myspace.c
index 5ee859dc0e..a1b66fd9ec 100644
--- a/libpurple/protocols/myspace/myspace.c
+++ b/libpurple/protocols/myspace/myspace.c
@@ -3094,7 +3094,9 @@ static PurplePluginProtocolInfo prpl_info = {
msim_get_account_text_table, /* get_account_text_table */
NULL, /* initiate_media */
NULL, /* get_media_caps */
- NULL /* get_moods */
+ NULL, /* get_moods */
+ NULL, /* set_public_alias */
+ NULL /* get_public_alias */
};
/**
diff --git a/libpurple/protocols/novell/novell.c b/libpurple/protocols/novell/novell.c
index d54757d631..0fc04298c0 100644
--- a/libpurple/protocols/novell/novell.c
+++ b/libpurple/protocols/novell/novell.c
@@ -3530,7 +3530,9 @@ static PurplePluginProtocolInfo prpl_info = {
NULL, /* get_account_text_table */
NULL, /* initiate_media */
NULL, /* get_media_caps */
- NULL /* get_moods */
+ NULL, /* get_moods */
+ NULL, /* set_public_alias */
+ NULL /* get_public_alias */
};
static PurplePluginInfo info = {
diff --git a/libpurple/protocols/null/nullprpl.c b/libpurple/protocols/null/nullprpl.c
index 6ce4663587..e2f998d251 100644
--- a/libpurple/protocols/null/nullprpl.c
+++ b/libpurple/protocols/null/nullprpl.c
@@ -997,7 +997,7 @@ static PurpleRoomlist *nullprpl_roomlist_get_list(PurpleConnection *gc) {
/* This cast is OK because this list is only staying around for the life
* of this function and none of the conversations are being deleted
- * in that timespan. */
+ * in that timespan. */
seen_ids = g_list_prepend(seen_ids, (char *)name); /* no, it's new. */
purple_debug_info("nullprpl", "%s (%d), ", name, id);
@@ -1117,10 +1117,12 @@ static PurplePluginProtocolInfo prpl_info =
NULL, /* send_attention */
NULL, /* get_attention_types */
sizeof(PurplePluginProtocolInfo), /* struct_size */
- NULL, /* get_account_text_table */
+ 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_moods */
};
static void nullprpl_init(PurplePlugin *plugin)
diff --git a/libpurple/protocols/oscar/Makefile.am b/libpurple/protocols/oscar/Makefile.am
index 5dfcba37df..4cf5e4995b 100644
--- a/libpurple/protocols/oscar/Makefile.am
+++ b/libpurple/protocols/oscar/Makefile.am
@@ -6,10 +6,11 @@ EXTRA_DIST = \
pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
OSCARSOURCES = \
+ authorization.c \
bstream.c \
clientlogin.c \
+ encoding.c \
family_admin.c \
- family_advert.c \
family_alert.c \
family_auth.c \
family_bart.c \
@@ -19,14 +20,11 @@ OSCARSOURCES = \
family_chatnav.c \
family_icq.c \
family_icbm.c \
- family_invite.c \
family_locate.c \
- family_odir.c \
family_oservice.c \
family_popup.c \
family_feedbag.c \
family_stats.c \
- family_translate.c \
family_userlookup.c \
flap_connection.c \
misc.c \
@@ -44,7 +42,9 @@ OSCARSOURCES = \
snac.c \
snactypes.h \
tlv.c \
- util.c
+ userinfo.c \
+ util.c \
+ visibility.c
AM_CFLAGS = $(st)
diff --git a/libpurple/protocols/oscar/Makefile.mingw b/libpurple/protocols/oscar/Makefile.mingw
index e9b7edd5fd..fa70dd433f 100644
--- a/libpurple/protocols/oscar/Makefile.mingw
+++ b/libpurple/protocols/oscar/Makefile.mingw
@@ -41,10 +41,11 @@ LIB_PATHS += -L$(GTK_TOP)/lib \
## SOURCES, OBJECTS
##
C_SRC = \
+ authorization.c \
bstream.c \
clientlogin.c \
+ encoding.c \
family_admin.c \
- family_advert.c \
family_alert.c \
family_auth.c \
family_bart.c \
@@ -52,16 +53,13 @@ C_SRC = \
family_buddy.c \
family_chat.c \
family_chatnav.c \
- family_icq.c \
+ family_feedbag.c \
family_icbm.c \
- family_invite.c \
+ family_icq.c \
family_locate.c \
- family_odir.c \
- family_popup.c \
family_oservice.c \
- family_feedbag.c \
+ family_popup.c \
family_stats.c \
- family_translate.c \
family_userlookup.c \
flap_connection.c \
misc.c \
@@ -75,7 +73,9 @@ C_SRC = \
rxhandlers.c \
snac.c \
tlv.c \
- util.c
+ userinfo.c \
+ util.c \
+ visibility.c
OBJECTS = $(C_SRC:%.c=%.o)
diff --git a/libpurple/protocols/oscar/authorization.c b/libpurple/protocols/oscar/authorization.c
new file mode 100644
index 0000000000..877605c3a2
--- /dev/null
+++ b/libpurple/protocols/oscar/authorization.c
@@ -0,0 +1,153 @@
+/*
+ * Purple's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
+*/
+
+/*
+ * Everything related to OSCAR authorization requests.
+ */
+
+#include "oscar.h"
+#include "request.h"
+
+static void
+oscar_auth_request(struct name_data *data, char *msg)
+{
+ PurpleConnection *gc;
+ OscarData *od;
+ PurpleAccount *account;
+ PurpleBuddy *buddy;
+ PurpleGroup *group;
+ const char *bname, *gname;
+
+ gc = data->gc;
+ od = purple_connection_get_protocol_data(gc);
+ account = purple_connection_get_account(gc);
+ buddy = purple_find_buddy(account, data->name);
+ if (buddy != NULL)
+ group = purple_buddy_get_group(buddy);
+ else
+ group = NULL;
+
+ if (group != NULL)
+ {
+ bname = purple_buddy_get_name(buddy);
+ gname = purple_group_get_name(group);
+ purple_debug_info("oscar", "ssi: adding buddy %s to group %s\n",
+ bname, gname);
+ aim_ssi_sendauthrequest(od, data->name, 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))
+ {
+ aim_ssi_addbuddy(od, bname, gname, NULL, purple_buddy_get_alias_only(buddy), NULL, NULL, TRUE);
+
+ /* Mobile users should always be online */
+ if (bname[0] == '+') {
+ purple_prpl_got_user_status(account,
+ purple_buddy_get_name(buddy),
+ OSCAR_STATUS_ID_AVAILABLE, NULL);
+ purple_prpl_got_user_status(account,
+ purple_buddy_get_name(buddy),
+ OSCAR_STATUS_ID_MOBILE, NULL);
+ }
+ }
+ }
+
+ oscar_free_name_data(data);
+}
+
+static void
+oscar_auth_grant(gpointer cbdata)
+{
+ struct name_data *data = cbdata;
+ PurpleConnection *gc = data->gc;
+ OscarData *od = purple_connection_get_protocol_data(gc);
+
+ aim_ssi_sendauthreply(od, data->name, 0x01, NULL);
+
+ oscar_free_name_data(data);
+}
+
+static void
+oscar_auth_dontgrant(struct name_data *data, char *msg)
+{
+ PurpleConnection *gc = data->gc;
+ OscarData *od = purple_connection_get_protocol_data(gc);
+
+ aim_ssi_sendauthreply(od, data->name, 0x00, msg ? msg : _("No reason given."));
+
+ 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);
+}
+
+/* When you ask other people for authorization */
+void
+oscar_auth_sendrequest(PurpleConnection *gc, const char *name)
+{
+ struct name_data *data;
+
+ data = g_new0(struct name_data, 1);
+ data->gc = gc;
+ data->name = g_strdup(name);
+
+ purple_request_input(data->gc, NULL, _("Authorization Request Message:"),
+ NULL, _("Please authorize me!"), TRUE, FALSE, NULL,
+ _("_OK"), G_CALLBACK(oscar_auth_request),
+ _("_Cancel"), G_CALLBACK(oscar_free_name_data),
+ purple_connection_get_account(gc), 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));
+
+ buddy = (PurpleBuddy *) node;
+ gc = purple_account_get_connection(purple_buddy_get_account(buddy));
+ oscar_auth_sendrequest(gc, purple_buddy_get_name(buddy));
+}
+
+/* When other people ask you for authorization */
+void
+oscar_auth_recvrequest(PurpleConnection *gc, gchar *name, gchar *nick, gchar *reason)
+{
+ PurpleAccount* account = purple_connection_get_account(gc);
+ struct name_data *data = g_new(struct name_data, 1);
+
+ data->gc = gc;
+ data->name = name;
+ 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);
+} \ No newline at end of file
diff --git a/libpurple/protocols/oscar/bstream.c b/libpurple/protocols/oscar/bstream.c
index cee0c87b54..31acac2724 100644
--- a/libpurple/protocols/oscar/bstream.c
+++ b/libpurple/protocols/oscar/bstream.c
@@ -24,7 +24,7 @@
#include "oscar.h"
-int byte_stream_new(ByteStream *bs, guint32 len)
+int byte_stream_new(ByteStream *bs, size_t len)
{
if (bs == NULL)
return -1;
@@ -32,9 +32,8 @@ int byte_stream_new(ByteStream *bs, guint32 len)
return byte_stream_init(bs, g_malloc(len), len);
}
-int byte_stream_init(ByteStream *bs, guint8 *data, int len)
+int byte_stream_init(ByteStream *bs, guint8 *data, size_t len)
{
-
if (bs == NULL)
return -1;
@@ -50,7 +49,7 @@ void byte_stream_destroy(ByteStream *bs)
g_free(bs->data);
}
-int byte_stream_empty(ByteStream *bs)
+int byte_stream_bytes_left(ByteStream *bs)
{
return bs->len - bs->offset;
}
@@ -60,229 +59,172 @@ int byte_stream_curpos(ByteStream *bs)
return bs->offset;
}
-int byte_stream_setpos(ByteStream *bs, unsigned int off)
+int byte_stream_setpos(ByteStream *bs, size_t off)
{
-
- if (off > bs->len)
- return -1;
+ g_return_val_if_fail(off <= bs->len, -1);
bs->offset = off;
-
return off;
}
void byte_stream_rewind(ByteStream *bs)
{
-
byte_stream_setpos(bs, 0);
-
- return;
}
/*
* N can be negative, which can be used for going backwards
- * in a bstream. I'm not sure if libfaim actually does
- * this anywhere...
+ * in a bstream.
*/
int byte_stream_advance(ByteStream *bs, int n)
{
-
- if ((byte_stream_curpos(bs) + n < 0) || (byte_stream_empty(bs) < n))
- return 0; /* XXX throw an exception */
+ g_return_val_if_fail(byte_stream_curpos(bs) + n >= 0, 0);
+ g_return_val_if_fail(n <= byte_stream_bytes_left(bs), 0);
bs->offset += n;
-
return n;
}
guint8 byte_stream_get8(ByteStream *bs)
{
-
- if (byte_stream_empty(bs) < 1)
- return 0; /* XXX throw an exception */
+ g_return_val_if_fail(byte_stream_bytes_left(bs) >= 1, 0);
bs->offset++;
-
return aimutil_get8(bs->data + bs->offset - 1);
}
guint16 byte_stream_get16(ByteStream *bs)
{
-
- if (byte_stream_empty(bs) < 2)
- return 0; /* XXX throw an exception */
+ g_return_val_if_fail(byte_stream_bytes_left(bs) >= 2, 0);
bs->offset += 2;
-
return aimutil_get16(bs->data + bs->offset - 2);
}
guint32 byte_stream_get32(ByteStream *bs)
{
-
- if (byte_stream_empty(bs) < 4)
- return 0; /* XXX throw an exception */
+ g_return_val_if_fail(byte_stream_bytes_left(bs) >= 4, 0);
bs->offset += 4;
-
return aimutil_get32(bs->data + bs->offset - 4);
}
guint8 byte_stream_getle8(ByteStream *bs)
{
-
- if (byte_stream_empty(bs) < 1)
- return 0; /* XXX throw an exception */
+ g_return_val_if_fail(byte_stream_bytes_left(bs) >= 1, 0);
bs->offset++;
-
return aimutil_getle8(bs->data + bs->offset - 1);
}
guint16 byte_stream_getle16(ByteStream *bs)
{
-
- if (byte_stream_empty(bs) < 2)
- return 0; /* XXX throw an exception */
+ g_return_val_if_fail(byte_stream_bytes_left(bs) >= 2, 0);
bs->offset += 2;
-
return aimutil_getle16(bs->data + bs->offset - 2);
}
guint32 byte_stream_getle32(ByteStream *bs)
{
-
- if (byte_stream_empty(bs) < 4)
- return 0; /* XXX throw an exception */
+ g_return_val_if_fail(byte_stream_bytes_left(bs) >= 4, 0);
bs->offset += 4;
-
return aimutil_getle32(bs->data + bs->offset - 4);
}
-static void byte_stream_getrawbuf_nocheck(ByteStream *bs, guint8 *buf, int len)
+static void byte_stream_getrawbuf_nocheck(ByteStream *bs, guint8 *buf, size_t len)
{
memcpy(buf, bs->data + bs->offset, len);
bs->offset += len;
}
-int byte_stream_getrawbuf(ByteStream *bs, guint8 *buf, int len)
+int byte_stream_getrawbuf(ByteStream *bs, guint8 *buf, size_t len)
{
-
- if (byte_stream_empty(bs) < len)
- return 0;
+ g_return_val_if_fail(byte_stream_bytes_left(bs) >= len, 0);
byte_stream_getrawbuf_nocheck(bs, buf, len);
return len;
}
-guint8 *byte_stream_getraw(ByteStream *bs, int len)
+guint8 *byte_stream_getraw(ByteStream *bs, size_t len)
{
guint8 *ob;
- if (byte_stream_empty(bs) < len)
- return NULL;
+ g_return_val_if_fail(byte_stream_bytes_left(bs) >= len, NULL);
ob = g_malloc(len);
-
byte_stream_getrawbuf_nocheck(bs, ob, len);
-
return ob;
}
-char *byte_stream_getstr(ByteStream *bs, int len)
+char *byte_stream_getstr(ByteStream *bs, size_t len)
{
char *ob;
- if (byte_stream_empty(bs) < len)
- return NULL;
+ g_return_val_if_fail(byte_stream_bytes_left(bs) >= len, NULL);
ob = g_malloc(len + 1);
-
byte_stream_getrawbuf_nocheck(bs, (guint8 *)ob, len);
-
ob[len] = '\0';
-
return ob;
}
int byte_stream_put8(ByteStream *bs, guint8 v)
{
-
- if (byte_stream_empty(bs) < 1)
- return 0; /* XXX throw an exception */
+ g_return_val_if_fail(byte_stream_bytes_left(bs) >= 1, 0);
bs->offset += aimutil_put8(bs->data + bs->offset, v);
-
return 1;
}
int byte_stream_put16(ByteStream *bs, guint16 v)
{
-
- if (byte_stream_empty(bs) < 2)
- return 0; /* XXX throw an exception */
+ g_return_val_if_fail(byte_stream_bytes_left(bs) >= 2, 0);
bs->offset += aimutil_put16(bs->data + bs->offset, v);
-
return 2;
}
int byte_stream_put32(ByteStream *bs, guint32 v)
{
-
- if (byte_stream_empty(bs) < 4)
- return 0; /* XXX throw an exception */
+ g_return_val_if_fail(byte_stream_bytes_left(bs) >= 4, 0);
bs->offset += aimutil_put32(bs->data + bs->offset, v);
-
return 1;
}
int byte_stream_putle8(ByteStream *bs, guint8 v)
{
-
- if (byte_stream_empty(bs) < 1)
- return 0; /* XXX throw an exception */
+ g_return_val_if_fail(byte_stream_bytes_left(bs) >= 1, 0);
bs->offset += aimutil_putle8(bs->data + bs->offset, v);
-
return 1;
}
int byte_stream_putle16(ByteStream *bs, guint16 v)
{
-
- if (byte_stream_empty(bs) < 2)
- return 0; /* XXX throw an exception */
+ g_return_val_if_fail(byte_stream_bytes_left(bs) >= 2, 0);
bs->offset += aimutil_putle16(bs->data + bs->offset, v);
-
return 2;
}
int byte_stream_putle32(ByteStream *bs, guint32 v)
{
-
- if (byte_stream_empty(bs) < 4)
- return 0; /* XXX throw an exception */
+ g_return_val_if_fail(byte_stream_bytes_left(bs) >= 4, 0);
bs->offset += aimutil_putle32(bs->data + bs->offset, v);
-
return 1;
}
-int byte_stream_putraw(ByteStream *bs, const guint8 *v, int len)
+int byte_stream_putraw(ByteStream *bs, const guint8 *v, size_t len)
{
-
- if (byte_stream_empty(bs) < len)
- return 0; /* XXX throw an exception */
+ g_return_val_if_fail(byte_stream_bytes_left(bs) >= len, 0);
memcpy(bs->data + bs->offset, v, len);
bs->offset += len;
-
return len;
}
@@ -291,19 +233,14 @@ int byte_stream_putstr(ByteStream *bs, const char *str)
return byte_stream_putraw(bs, (guint8 *)str, strlen(str));
}
-int byte_stream_putbs(ByteStream *bs, ByteStream *srcbs, int len)
+int byte_stream_putbs(ByteStream *bs, ByteStream *srcbs, size_t len)
{
-
- if (byte_stream_empty(srcbs) < len)
- return 0; /* XXX throw exception (underrun) */
-
- if (byte_stream_empty(bs) < len)
- return 0; /* XXX throw exception (overflow) */
+ g_return_val_if_fail(byte_stream_bytes_left(srcbs) >= len, 0);
+ g_return_val_if_fail(byte_stream_bytes_left(bs) >= len, 0);
memcpy(bs->data + bs->offset, srcbs->data + srcbs->offset, len);
bs->offset += len;
srcbs->offset += len;
-
return len;
}
diff --git a/libpurple/protocols/oscar/clientlogin.c b/libpurple/protocols/oscar/clientlogin.c
index 1ae6bc686a..9221e5c49d 100644
--- a/libpurple/protocols/oscar/clientlogin.c
+++ b/libpurple/protocols/oscar/clientlogin.c
@@ -428,6 +428,9 @@ static gboolean parse_client_login_response(PurpleConnection *gc, const gchar *r
"was %d (%d): %s\n", status_code, status_detail_code, response);
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_CONNECTION_ERROR_AUTHENTICATION_FAILED,
_("Incorrect password"));
@@ -515,8 +518,12 @@ static void client_login_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data
if (error_message != NULL || len == 0) {
gchar *tmp;
- tmp = g_strdup_printf(_("Error requesting %s: %s"),
- URL_CLIENT_LOGIN, error_message);
+ if (error_message != NULL)
+ tmp = g_strdup_printf(_("Error requesting %s: %s"),
+ URL_CLIENT_LOGIN, error_message);
+ else
+ tmp = g_strdup_printf(_("Error requesting %s"),
+ URL_CLIENT_LOGIN);
purple_connection_error_reason(gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
g_free(tmp);
diff --git a/libpurple/protocols/oscar/encoding.c b/libpurple/protocols/oscar/encoding.c
new file mode 100644
index 0000000000..985f2cb295
--- /dev/null
+++ b/libpurple/protocols/oscar/encoding.c
@@ -0,0 +1,235 @@
+/*
+ * Purple's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
+*/
+
+#include "encoding.h"
+
+static gchar *
+encoding_extract(const char *encoding)
+{
+ char *begin, *end;
+
+ if (encoding == NULL) {
+ return NULL;
+ }
+
+ if (!g_str_has_prefix(encoding, "text/aolrtf; charset=") &&
+ !g_str_has_prefix(encoding, "text/x-aolrtf; charset=") &&
+ !g_str_has_prefix(encoding, "text/plain; charset=")) {
+ return g_strdup(encoding);
+ }
+
+ begin = strchr(encoding, '"');
+ end = strrchr(encoding, '"');
+
+ if ((begin == NULL) || (end == NULL) || (begin >= end)) {
+ return g_strdup(encoding);
+ }
+
+ return g_strndup(begin+1, (end-1) - begin);
+}
+
+gchar *
+oscar_encoding_to_utf8(const char *encoding, const char *text, int textlen)
+{
+ gchar *utf8 = NULL;
+ const gchar *glib_encoding = NULL;
+ gchar *extracted_encoding = encoding_extract(encoding);
+
+ if (extracted_encoding == NULL || *extracted_encoding == '\0') {
+ purple_debug_info("oscar", "Empty encoding, assuming UTF-8\n");
+ } else if (!g_ascii_strcasecmp(extracted_encoding, "iso-8859-1")) {
+ glib_encoding = "iso-8859-1";
+ } else if (!g_ascii_strcasecmp(extracted_encoding, "ISO-8859-1-Windows-3.1-Latin-1") || !g_ascii_strcasecmp(extracted_encoding, "us-ascii")) {
+ glib_encoding = "Windows-1252";
+ } else if (!g_ascii_strcasecmp(extracted_encoding, "unicode-2-0")) {
+ glib_encoding = "UTF-16BE";
+ } else if (g_ascii_strcasecmp(extracted_encoding, "utf-8")) {
+ purple_debug_warning("oscar", "Unrecognized character encoding \"%s\", attempting to convert to UTF-8 anyway\n", extracted_encoding);
+ glib_encoding = extracted_encoding;
+ }
+
+ if (glib_encoding != NULL) {
+ utf8 = g_convert(text, textlen, "UTF-8", glib_encoding, NULL, NULL, NULL);
+ }
+
+ /*
+ * If utf8 is still NULL then either the encoding is utf-8 or
+ * we have been unable to convert the text to utf-8 from the encoding
+ * that was specified. So we check if the text is valid utf-8 then
+ * just copy it.
+ */
+ if (utf8 == NULL) {
+ if (textlen != 0 && *text != '\0' && !g_utf8_validate(text, textlen, NULL))
+ utf8 = g_strdup(_("(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.)"));
+ else
+ utf8 = g_strndup(text, textlen);
+ }
+
+ g_free(extracted_encoding);
+ return utf8;
+}
+
+gchar *
+oscar_utf8_try_convert(PurpleAccount *account, OscarData *od, const gchar *msg)
+{
+ const char *charset = NULL;
+ char *ret = NULL;
+
+ if (msg == NULL)
+ return NULL;
+
+ if (g_utf8_validate(msg, -1, NULL))
+ return g_strdup(msg);
+
+ if (od->icq)
+ charset = purple_account_get_string(account, "encoding", NULL);
+
+ if(charset && *charset)
+ ret = g_convert(msg, -1, "UTF-8", charset, NULL, NULL, NULL);
+
+ if(!ret)
+ ret = purple_utf8_try_convert(msg);
+
+ return ret;
+}
+
+static gchar *
+oscar_convert_to_utf8(const gchar *data, gsize datalen, const char *charsetstr, gboolean fallback)
+{
+ gchar *ret = NULL;
+ GError *err = NULL;
+
+ if ((charsetstr == NULL) || (*charsetstr == '\0'))
+ return NULL;
+
+ if (g_ascii_strcasecmp("UTF-8", charsetstr)) {
+ if (fallback)
+ ret = g_convert_with_fallback(data, datalen, "UTF-8", charsetstr, "?", NULL, NULL, &err);
+ else
+ ret = g_convert(data, datalen, "UTF-8", charsetstr, NULL, NULL, &err);
+ if (err != NULL) {
+ purple_debug_warning("oscar", "Conversion from %s failed: %s.\n",
+ charsetstr, err->message);
+ g_error_free(err);
+ }
+ } else {
+ if (g_utf8_validate(data, datalen, NULL))
+ ret = g_strndup(data, datalen);
+ else
+ purple_debug_warning("oscar", "String is not valid UTF-8.\n");
+ }
+
+ return ret;
+}
+
+gchar *
+oscar_decode_im(PurpleAccount *account, const char *sourcebn, guint16 charset, const gchar *data, gsize datalen)
+{
+ gchar *ret = NULL;
+ /* charsetstr1 is always set to what the correct encoding should be. */
+ const gchar *charsetstr1, *charsetstr2, *charsetstr3 = NULL;
+
+ if ((datalen == 0) || (data == NULL))
+ return NULL;
+
+ if (charset == AIM_CHARSET_UNICODE) {
+ charsetstr1 = "UTF-16BE";
+ charsetstr2 = "UTF-8";
+ } else if (charset == AIM_CHARSET_LATIN_1) {
+ if ((sourcebn != NULL) && oscar_util_valid_name_icq(sourcebn))
+ charsetstr1 = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
+ else
+ charsetstr1 = "ISO-8859-1";
+ charsetstr2 = "UTF-8";
+ } else if (charset == AIM_CHARSET_ASCII) {
+ /* Should just be "ASCII" */
+ charsetstr1 = "ASCII";
+ charsetstr2 = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
+ } else if (charset == 0x000d) {
+ /* iChat sending unicode over a Direct IM connection = UTF-8 */
+ /* Mobile AIM client on multiple devices (including Blackberry Tour, Nokia 3100, and LG VX6000) = ISO-8859-1 */
+ charsetstr1 = "UTF-8";
+ charsetstr2 = "ISO-8859-1";
+ charsetstr3 = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
+ } else {
+ /* Unknown, hope for valid UTF-8... */
+ charsetstr1 = "UTF-8";
+ charsetstr2 = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
+ }
+
+ purple_debug_info("oscar", "Parsing IM, charset=0x%04hx, datalen=%" G_GSIZE_FORMAT ", choice1=%s, choice2=%s, choice3=%s\n",
+ charset, datalen, charsetstr1, charsetstr2, (charsetstr3 ? charsetstr3 : ""));
+
+ ret = oscar_convert_to_utf8(data, datalen, charsetstr1, FALSE);
+ if (ret == NULL) {
+ if (charsetstr3 != NULL) {
+ /* Try charsetstr2 without allowing substitutions, then fall through to charsetstr3 if needed */
+ ret = oscar_convert_to_utf8(data, datalen, charsetstr2, FALSE);
+ if (ret == NULL)
+ ret = oscar_convert_to_utf8(data, datalen, charsetstr3, TRUE);
+ } else {
+ /* Try charsetstr2, allowing substitutions */
+ ret = oscar_convert_to_utf8(data, datalen, charsetstr2, TRUE);
+ }
+ }
+ if (ret == NULL) {
+ char *str, *salvage, *tmp;
+
+ str = g_malloc(datalen + 1);
+ strncpy(str, data, datalen);
+ str[datalen] = '\0';
+ salvage = purple_utf8_salvage(str);
+ tmp = g_strdup_printf(_("(There was an error receiving this message. Either you and %s have different encodings selected, or %s has a buggy client.)"),
+ sourcebn, sourcebn);
+ ret = g_strdup_printf("%s %s", salvage, tmp);
+ g_free(tmp);
+ g_free(str);
+ g_free(salvage);
+ }
+
+ return ret;
+}
+
+static guint16
+get_simplest_charset(const char *utf8)
+{
+ while (*utf8)
+ {
+ if ((unsigned char)(*utf8) > 0x7f) {
+ /* not ASCII! */
+ return AIM_CHARSET_UNICODE;
+ }
+ utf8++;
+ }
+ return AIM_CHARSET_ASCII;
+}
+
+gchar *
+oscar_encode_im(const gchar *msg, gsize *result_len, guint16 *charset, gchar **charsetstr)
+{
+ guint16 msg_charset = get_simplest_charset(msg);
+ if (charset != NULL) {
+ *charset = msg_charset;
+ }
+ if (charsetstr != NULL) {
+ *charsetstr = msg_charset == AIM_CHARSET_ASCII ? "us-ascii" : "unicode-2-0";
+ }
+ return g_convert(msg, -1, msg_charset == AIM_CHARSET_ASCII ? "ASCII" : "UTF-16BE", "UTF-8", NULL, result_len, NULL);
+}
diff --git a/libpurple/protocols/oscar/encoding.h b/libpurple/protocols/oscar/encoding.h
new file mode 100644
index 0000000000..5f47ec47bf
--- /dev/null
+++ b/libpurple/protocols/oscar/encoding.h
@@ -0,0 +1,46 @@
+/*
+ * Purple's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
+*/
+
+#ifndef _ENCODING_H_
+#define _ENCODING_H_
+
+#include "oscar.h"
+#include "oscarcommon.h"
+
+gchar * oscar_encoding_to_utf8(const char *encoding, const char *text, int textlen);
+gchar * oscar_utf8_try_convert(PurpleAccount *account, OscarData *od, const gchar *msg);
+
+/**
+ * This attemps to decode an incoming IM into a UTF8 string.
+ *
+ * We try decoding using two different character sets. The charset
+ * specified in the IM determines the order in which we attempt to
+ * decode. We do this because there are lots of broken ICQ clients
+ * that don't correctly send non-ASCII messages. And if Purple isn't
+ * able to deal with that crap, then people complain like banshees.
+ */
+gchar * oscar_decode_im(PurpleAccount *account, const char *sourcebn, guint16 charset, const gchar *data, gsize datalen);
+
+/**
+ * Figure out what encoding to use when sending a given outgoing message.
+ */
+gchar * oscar_encode_im(const gchar *msg, gsize *result_len, guint16 *charset, gchar **charsetstr);
+
+#endif \ No newline at end of file
diff --git a/libpurple/protocols/oscar/family_admin.c b/libpurple/protocols/oscar/family_admin.c
index c276f12cb9..d199a789c1 100644
--- a/libpurple/protocols/oscar/family_admin.c
+++ b/libpurple/protocols/oscar/family_admin.c
@@ -47,7 +47,7 @@ aim_admin_getinfo(OscarData *od, FlapConnection *conn, guint16 info)
byte_stream_put16(&bs, 0x0000);
snacid = aim_cachesnac(od, SNAC_FAMILY_ADMIN, 0x0002, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ADMIN, 0x0002, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ADMIN, 0x0002, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -68,7 +68,7 @@ infochange(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *fr
perms = byte_stream_get16(bs);
tlvcount = byte_stream_get16(bs);
- while (tlvcount && byte_stream_empty(bs)) {
+ while (tlvcount && byte_stream_bytes_left(bs)) {
guint16 type, length;
type = byte_stream_get16(bs);
@@ -127,7 +127,7 @@ aim_admin_setnick(OscarData *od, FlapConnection *conn, const char *newnick)
aim_tlvlist_free(tlvlist);
snacid = aim_cachesnac(od, SNAC_FAMILY_ADMIN, 0x0004, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ADMIN, 0x0004, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ADMIN, 0x0004, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -154,7 +154,7 @@ aim_admin_changepasswd(OscarData *od, FlapConnection *conn, const char *newpw, c
aim_tlvlist_free(tlvlist);
snacid = aim_cachesnac(od, SNAC_FAMILY_ADMIN, 0x0004, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ADMIN, 0x0004, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ADMIN, 0x0004, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -177,7 +177,7 @@ aim_admin_setemail(OscarData *od, FlapConnection *conn, const char *newemail)
aim_tlvlist_free(tlvlist);
snacid = aim_cachesnac(od, SNAC_FAMILY_ADMIN, 0x0004, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ADMIN, 0x0004, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ADMIN, 0x0004, snacid, &bs);
byte_stream_destroy(&bs);
}
diff --git a/libpurple/protocols/oscar/family_advert.c b/libpurple/protocols/oscar/family_advert.c
deleted file mode 100644
index fcd1677ce6..0000000000
--- a/libpurple/protocols/oscar/family_advert.c
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Purple's oscar protocol plugin
- * This file is the legal property of its developers.
- * Please see the AUTHORS file distributed alongside this file.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
-*/
-
-/*
- * Family 0x0005 - Advertisements.
- *
- */
-
-#include "oscar.h"
-
-void
-aim_ads_requestads(OscarData *od, FlapConnection *conn)
-{
- aim_genericreq_n(od, conn, SNAC_FAMILY_ADVERT, 0x0002);
-}
-
-static int snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *rx, aim_modsnac_t *snac, ByteStream *bs)
-{
- return 0;
-}
-
-int adverts_modfirst(OscarData *od, aim_module_t *mod)
-{
-
- mod->family = SNAC_FAMILY_ADVERT;
- mod->version = 0x0001;
- mod->toolid = 0x0001;
- mod->toolversion = 0x0001;
- mod->flags = 0;
- strncpy(mod->name, "advert", sizeof(mod->name));
- mod->snachandler = snachandler;
-
- return 0;
-}
diff --git a/libpurple/protocols/oscar/family_alert.c b/libpurple/protocols/oscar/family_alert.c
index df6f909d7a..58f535a7a2 100644
--- a/libpurple/protocols/oscar/family_alert.c
+++ b/libpurple/protocols/oscar/family_alert.c
@@ -73,7 +73,7 @@ aim_email_sendcookies(OscarData *od)
byte_stream_put16(&bs, 0x0631);
snacid = aim_cachesnac(od, SNAC_FAMILY_ALERT, 0x0006, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ALERT, 0x0006, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ALERT, 0x0006, snacid, &bs);
byte_stream_destroy(&bs);
@@ -189,7 +189,7 @@ aim_email_activate(OscarData *od)
byte_stream_put32(&bs, 0x00000000);
snacid = aim_cachesnac(od, SNAC_FAMILY_ALERT, 0x0016, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ALERT, 0x0006, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ALERT, 0x0006, snacid, &bs);
byte_stream_destroy(&bs);
diff --git a/libpurple/protocols/oscar/family_auth.c b/libpurple/protocols/oscar/family_auth.c
index db7a1a0920..ac011df63d 100644
--- a/libpurple/protocols/oscar/family_auth.c
+++ b/libpurple/protocols/oscar/family_auth.c
@@ -56,17 +56,10 @@ static int
aim_encode_password(const char *password, guint8 *encoded)
{
guint8 encoding_table[] = {
-#if 0 /* old v1 table */
- 0xf3, 0xb3, 0x6c, 0x99,
- 0x95, 0x3f, 0xac, 0xb6,
- 0xc5, 0xfa, 0x6b, 0x63,
- 0x69, 0x6c, 0xc3, 0x9f
-#else /* v2.1 table, also works for ICQ */
0xf3, 0x26, 0x81, 0xc4,
0x39, 0x86, 0xdb, 0x92,
0x71, 0xa3, 0xb9, 0xe6,
0x53, 0x7a, 0x95, 0x7c
-#endif
};
unsigned int i;
@@ -234,7 +227,7 @@ aim_send_login(OscarData *od, FlapConnection *conn, const char *sn, const char *
frame = flap_frame_new(od, 0x02, 1152);
snacid = aim_cachesnac(od, SNAC_FAMILY_AUTH, 0x0002, 0x0000, NULL, 0);
- aim_putsnac(&frame->data, SNAC_FAMILY_AUTH, 0x0002, 0x0000, snacid);
+ aim_putsnac(&frame->data, SNAC_FAMILY_AUTH, 0x0002, snacid);
aim_tlvlist_add_str(&tlvlist, 0x0001, sn);
@@ -385,12 +378,6 @@ parse(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame,
if (aim_tlv_gettlv(tlvlist, 0x0043, 1))
info->latestbeta.name = aim_tlv_getstr(tlvlist, 0x0043, 1);
-#if 0
- if (aim_tlv_gettlv(tlvlist, 0x0048, 1)) {
- /* beta serial */
- }
-#endif
-
if (aim_tlv_gettlv(tlvlist, 0x0044, 1))
info->latestrelease.build = aim_tlv_get32(tlvlist, 0x0044, 1);
if (aim_tlv_gettlv(tlvlist, 0x0045, 1))
@@ -400,27 +387,12 @@ parse(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame,
if (aim_tlv_gettlv(tlvlist, 0x0047, 1))
info->latestrelease.name = aim_tlv_getstr(tlvlist, 0x0047, 1);
-#if 0
- if (aim_tlv_gettlv(tlvlist, 0x0049, 1)) {
- /* lastest release serial */
- }
-#endif
-
/*
* URL to change password.
*/
if (aim_tlv_gettlv(tlvlist, 0x0054, 1))
info->chpassurl = aim_tlv_getstr(tlvlist, 0x0054, 1);
-#if 0
- /*
- * Unknown. Seen on an @mac.com username with value of 0x003f
- */
- if (aim_tlv_gettlv(tlvlist, 0x0055, 1)) {
- /* Unhandled */
- }
-#endif
-
od->authinfo = info;
if ((userfunc = aim_callhandler(od, snac ? snac->family : SNAC_FAMILY_AUTH, snac ? snac->subtype : 0x0003)))
@@ -504,7 +476,7 @@ aim_request_login(OscarData *od, FlapConnection *conn, const char *sn)
frame = flap_frame_new(od, 0x02, 10+2+2+strlen(sn)+8);
snacid = aim_cachesnac(od, SNAC_FAMILY_AUTH, 0x0006, 0x0000, NULL, 0);
- aim_putsnac(&frame->data, SNAC_FAMILY_AUTH, 0x0006, 0x0000, snacid);
+ aim_putsnac(&frame->data, SNAC_FAMILY_AUTH, 0x0006, snacid);
aim_tlvlist_add_str(&tlvlist, 0x0001, sn);
@@ -602,7 +574,7 @@ aim_auth_securid_send(OscarData *od, const char *securid)
frame = flap_frame_new(od, 0x02, 10+2+len);
snacid = aim_cachesnac(od, SNAC_FAMILY_AUTH, SNAC_SUBTYPE_AUTH_SECURID_RESPONSE, 0x0000, NULL, 0);
- aim_putsnac(&frame->data, SNAC_FAMILY_AUTH, SNAC_SUBTYPE_AUTH_SECURID_RESPONSE, 0x0000, 0);
+ aim_putsnac(&frame->data, SNAC_FAMILY_AUTH, SNAC_SUBTYPE_AUTH_SECURID_RESPONSE, 0);
byte_stream_put16(&frame->data, len);
byte_stream_putstr(&frame->data, securid);
diff --git a/libpurple/protocols/oscar/family_bart.c b/libpurple/protocols/oscar/family_bart.c
index ee388456bd..cd7ca43839 100644
--- a/libpurple/protocols/oscar/family_bart.c
+++ b/libpurple/protocols/oscar/family_bart.c
@@ -56,7 +56,7 @@ aim_bart_upload(OscarData *od, const guint8 *icon, guint16 iconlen)
byte_stream_putraw(&bs, icon, iconlen);
snacid = aim_cachesnac(od, SNAC_FAMILY_BART, 0x0002, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_BART, 0x0002, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_BART, 0x0002, snacid, &bs);
byte_stream_destroy(&bs);
@@ -121,7 +121,7 @@ aim_bart_request(OscarData *od, const char *bn, guint8 iconcsumtype, const guint
byte_stream_putraw(&bs, iconcsum, iconcsumlen);
snacid = aim_cachesnac(od, SNAC_FAMILY_BART, 0x0004, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_BART, 0x0004, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_BART, 0x0004, snacid, &bs);
byte_stream_destroy(&bs);
diff --git a/libpurple/protocols/oscar/family_bos.c b/libpurple/protocols/oscar/family_bos.c
index 2790b2ed82..16defd2076 100644
--- a/libpurple/protocols/oscar/family_bos.c
+++ b/libpurple/protocols/oscar/family_bos.c
@@ -68,98 +68,6 @@ static int rights(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFr
return ret;
}
-/*
- * Subtype 0x0004 - Set group permission mask.
- *
- * Normally 0x1f (all classes).
- *
- * The group permission mask allows you to keep users of a certain
- * class or classes from talking to you. The mask should be
- * a bitwise OR of all the user classes you want to see you.
- *
- */
-void
-aim_bos_setgroupperm(OscarData *od, FlapConnection *conn, guint32 mask)
-{
- aim_genericreq_l(od, conn, SNAC_FAMILY_BOS, 0x0004, &mask);
-}
-
-/*
- * Stubtypes 0x0005, 0x0006, 0x0007, and 0x0008 - Modify permit/deny lists.
- *
- * Changes your visibility depending on changetype:
- *
- * AIM_VISIBILITYCHANGE_PERMITADD: Lets provided list of names see you
- * AIM_VISIBILITYCHANGE_PERMIDREMOVE: Removes listed names from permit list
- * AIM_VISIBILITYCHANGE_DENYADD: Hides you from provided list of names
- * AIM_VISIBILITYCHANGE_DENYREMOVE: Lets list see you again
- *
- * list should be a list of "Buddy Name One&BuddyNameTwo&" etc.
- *
- * Equivelents to options in WinAIM:
- * - Allow all users to contact me: Send an AIM_VISIBILITYCHANGE_DENYADD
- * with only your name on it.
- * - Allow only users on my Buddy List: Send an
- * AIM_VISIBILITYCHANGE_PERMITADD with the list the same as your
- * buddy list
- * - Allow only the uesrs below: Send an AIM_VISIBILITYCHANGE_PERMITADD
- * with everyone listed that you want to see you.
- * - Block all users: Send an AIM_VISIBILITYCHANGE_PERMITADD with only
- * yourself in the list
- * - Block the users below: Send an AIM_VISIBILITYCHANGE_DENYADD with
- * the list of users to be blocked
- *
- * XXX ye gods.
- */
-int aim_bos_changevisibility(OscarData *od, FlapConnection *conn, int changetype, const char *denylist)
-{
- ByteStream bs;
- int packlen = 0;
- guint16 subtype;
- char *localcpy = NULL, *tmpptr = NULL;
- int i;
- int listcount;
- aim_snacid_t snacid;
-
- if (!denylist)
- return -EINVAL;
-
- if (changetype == AIM_VISIBILITYCHANGE_PERMITADD)
- subtype = 0x05;
- else if (changetype == AIM_VISIBILITYCHANGE_PERMITREMOVE)
- subtype = 0x06;
- else if (changetype == AIM_VISIBILITYCHANGE_DENYADD)
- subtype = 0x07;
- else if (changetype == AIM_VISIBILITYCHANGE_DENYREMOVE)
- subtype = 0x08;
- else
- return -EINVAL;
-
- localcpy = g_strdup(denylist);
-
- listcount = aimutil_itemcnt(localcpy, '&');
- packlen = aimutil_tokslen(localcpy, 99, '&') + listcount-1;
-
- byte_stream_new(&bs, packlen);
-
- for (i = 0; (i < (listcount - 1)) && (i < 99); i++) {
- tmpptr = aimutil_itemindex(localcpy, i, '&');
-
- byte_stream_put8(&bs, strlen(tmpptr));
- byte_stream_putstr(&bs, tmpptr);
-
- g_free(tmpptr);
- }
- g_free(localcpy);
-
- snacid = aim_cachesnac(od, SNAC_FAMILY_BOS, subtype, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_BOS, subtype, 0x0000, snacid, &bs);
-
- byte_stream_destroy(&bs);
-
- return 0;
-}
-
static int
snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
{
diff --git a/libpurple/protocols/oscar/family_buddy.c b/libpurple/protocols/oscar/family_buddy.c
index 7efd60e520..a58232a058 100644
--- a/libpurple/protocols/oscar/family_buddy.c
+++ b/libpurple/protocols/oscar/family_buddy.c
@@ -88,117 +88,6 @@ rights(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame,
}
/*
- * Subtype 0x0004 (SNAC_SUBTYPE_BUDDY_ADDBUDDY) - Add buddy to list.
- *
- * Adds a single buddy to your buddy list after login.
- * XXX This should just be an extension of setbuddylist()
- *
- */
-int
-aim_buddylist_addbuddy(OscarData *od, FlapConnection *conn, const char *sn)
-{
- ByteStream bs;
- aim_snacid_t snacid;
-
- if (!sn || !strlen(sn))
- return -EINVAL;
-
- byte_stream_new(&bs, 1+strlen(sn));
-
- byte_stream_put8(&bs, strlen(sn));
- byte_stream_putstr(&bs, sn);
-
- snacid = aim_cachesnac(od, SNAC_FAMILY_BUDDY, 0x0004, 0x0000, sn, strlen(sn)+1);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_BUDDY, 0x0004, 0x0000, snacid, &bs);
-
- byte_stream_destroy(&bs);
-
- return 0;
-}
-
-/*
- * Subtype 0x0004 (SNAC_SUBTYPE_BUDDY_ADDBUDDY) - Add multiple buddies to your buddy list.
- *
- * This just builds the "set buddy list" command then queues it.
- *
- * buddy_list = "Buddy Name One&BuddyNameTwo&";
- *
- * XXX Clean this up.
- *
- */
-int
-aim_buddylist_set(OscarData *od, FlapConnection *conn, const char *buddy_list)
-{
- ByteStream bs;
- aim_snacid_t snacid;
- int len = 0;
- char *localcpy = NULL;
- char *tmpptr = NULL;
-
- if (!buddy_list || !(localcpy = g_strdup(buddy_list)))
- return -EINVAL;
-
- for (tmpptr = strtok(localcpy, "&"); tmpptr; ) {
- purple_debug_misc("oscar", "---adding: %s (%" G_GSIZE_FORMAT
- ")\n", tmpptr, strlen(tmpptr));
- len += 1 + strlen(tmpptr);
- tmpptr = strtok(NULL, "&");
- }
-
- byte_stream_new(&bs, len);
-
- strncpy(localcpy, buddy_list, strlen(buddy_list) + 1);
-
- for (tmpptr = strtok(localcpy, "&"); tmpptr; ) {
-
- purple_debug_misc("oscar", "---adding: %s (%" G_GSIZE_FORMAT
- ")\n", tmpptr, strlen(tmpptr));
-
- byte_stream_put8(&bs, strlen(tmpptr));
- byte_stream_putstr(&bs, tmpptr);
- tmpptr = strtok(NULL, "&");
- }
-
- snacid = aim_cachesnac(od, SNAC_FAMILY_BUDDY, 0x0004, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_BUDDY, 0x0004, 0x0000, snacid, &bs);
-
- byte_stream_destroy(&bs);
-
- g_free(localcpy);
-
- return 0;
-}
-
-/*
- * Subtype 0x0005 (SNAC_SUBTYPE_BUDDY_REMBUDDY) - Remove buddy from list.
- *
- * XXX generalise to support removing multiple buddies (basically, its
- * the same as setbuddylist() but with a different snac subtype).
- *
- */
-int
-aim_buddylist_removebuddy(OscarData *od, FlapConnection *conn, const char *sn)
-{
- ByteStream bs;
- aim_snacid_t snacid;
-
- if (!sn || !strlen(sn))
- return -EINVAL;
-
- byte_stream_new(&bs, 1 + strlen(sn));
-
- byte_stream_put8(&bs, strlen(sn));
- byte_stream_putstr(&bs, sn);
-
- snacid = aim_cachesnac(od, SNAC_FAMILY_BUDDY, 0x0005, 0x0000, sn, strlen(sn)+1);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_BUDDY, 0x0005, 0x0000, snacid, &bs);
-
- byte_stream_destroy(&bs);
-
- return 0;
-}
-
-/*
* Subtypes 0x000b (SNAC_SUBTYPE_BUDDY_ONCOMING) and 0x000c (SNAC_SUBTYPE_BUDDY_OFFGOING) - Change in buddy status
*
* Oncoming Buddy notifications contain a subset of the
diff --git a/libpurple/protocols/oscar/family_chat.c b/libpurple/protocols/oscar/family_chat.c
index 9e25b22cc3..89fa331c0c 100644
--- a/libpurple/protocols/oscar/family_chat.c
+++ b/libpurple/protocols/oscar/family_chat.c
@@ -47,74 +47,6 @@ flap_connection_destroy_chat(OscarData *od, FlapConnection *conn)
return;
}
-char *
-aim_chat_getname(FlapConnection *conn)
-{
- struct chatconnpriv *ccp;
-
- if (!conn)
- return NULL;
-
- if (conn->type != SNAC_FAMILY_CHAT)
- return NULL;
-
- ccp = (struct chatconnpriv *)conn->internal;
-
- return ccp->name;
-}
-
-/* XXX get this into conn.c -- evil!! */
-FlapConnection *
-aim_chat_getconn(OscarData *od, const char *name)
-{
- GSList *cur;
-
- for (cur = od->oscar_connections; cur; cur = cur->next)
- {
- FlapConnection *conn;
- struct chatconnpriv *ccp;
-
- conn = cur->data;
- ccp = (struct chatconnpriv *)conn->internal;
-
- if (conn->type != SNAC_FAMILY_CHAT)
- continue;
- if (!conn->internal)
- {
- purple_debug_misc("oscar", "%sfaim: chat: chat connection with no name! (fd = %d)\n",
- conn->gsc ? "(ssl) " : "", conn->gsc ? conn->gsc->fd : conn->fd);
- continue;
- }
-
- if (strcmp(ccp->name, name) == 0)
- return conn;
- }
-
- return NULL;
-}
-
-int
-aim_chat_attachname(FlapConnection *conn, guint16 exchange, const char *roomname, guint16 instance)
-{
- struct chatconnpriv *ccp;
-
- if (!conn || !roomname)
- return -EINVAL;
-
- if (conn->internal)
- g_free(conn->internal);
-
- ccp = g_new(struct chatconnpriv, 1);
-
- ccp->exchange = exchange;
- ccp->name = g_strdup(roomname);
- ccp->instance = instance;
-
- conn->internal = (void *)ccp;
-
- return 0;
-}
-
int
aim_chat_readroominfo(ByteStream *bs, struct aim_chat_roominfo *outinfo)
{
@@ -129,19 +61,6 @@ aim_chat_readroominfo(ByteStream *bs, struct aim_chat_roominfo *outinfo)
return 0;
}
-int
-aim_chat_leaveroom(OscarData *od, const char *name)
-{
- FlapConnection *conn;
-
- if (!(conn = aim_chat_getconn(od, name)))
- return -ENOENT;
-
- flap_connection_close(od, conn);
-
- return 0;
-}
-
/*
* Subtype 0x0002 - General room information. Lots of stuff.
*
@@ -153,21 +72,12 @@ aim_chat_leaveroom(OscarData *od, const char *name)
static int
infoupdate(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
{
- aim_userinfo_t *userinfo = NULL;
aim_rxcallback_t userfunc;
int ret = 0;
- int usercount;
guint8 detaillevel = 0;
- char *roomname;
struct aim_chat_roominfo roominfo;
- guint16 tlvcount = 0;
GSList *tlvlist;
- aim_tlv_t *tlv;
- char *roomdesc;
- guint16 flags;
- guint32 creationtime;
guint16 maxmsglen, maxvisiblemsglen;
- guint16 unknown_d2, unknown_d5;
aim_chat_readroominfo(bs, &roominfo);
@@ -178,139 +88,27 @@ infoupdate(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *fr
return 1;
}
- tlvcount = byte_stream_get16(bs);
-
/*
* Everything else are TLVs.
*/
tlvlist = aim_tlvlist_read(bs);
/*
- * TLV type 0x006a is the room name in Human Readable Form.
- */
- roomname = aim_tlv_getstr(tlvlist, 0x006a, 1);
-
- /*
- * Type 0x006f: Number of occupants.
- */
- usercount = aim_tlv_get16(tlvlist, 0x006f, 1);
-
- /*
- * Type 0x0073: Occupant list.
- */
- tlv = aim_tlv_gettlv(tlvlist, 0x0073, 1);
- if (tlv != NULL)
- {
- int curoccupant = 0;
- ByteStream occbs;
-
- /* Allocate enough userinfo structs for all occupants */
- userinfo = g_new0(aim_userinfo_t, usercount);
-
- byte_stream_init(&occbs, tlv->value, tlv->length);
-
- while (curoccupant < usercount)
- aim_info_extract(od, &occbs, &userinfo[curoccupant++]);
- }
-
- /*
- * Type 0x00c9: Flags. (AIM_CHATROOM_FLAG)
- */
- flags = aim_tlv_get16(tlvlist, 0x00c9, 1);
-
- /*
- * Type 0x00ca: Creation time (4 bytes)
- */
- creationtime = aim_tlv_get32(tlvlist, 0x00ca, 1);
-
- /*
* Type 0x00d1: Maximum Message Length
*/
maxmsglen = aim_tlv_get16(tlvlist, 0x00d1, 1);
/*
- * Type 0x00d2: Unknown. (2 bytes)
- */
- unknown_d2 = aim_tlv_get16(tlvlist, 0x00d2, 1);
-
- /*
- * Type 0x00d3: Room Description
- */
- roomdesc = aim_tlv_getstr(tlvlist, 0x00d3, 1);
-
-#if 0
- /*
- * Type 0x000d4: Unknown (flag only)
- */
- if (aim_tlv_gettlv(tlvlist, 0x000d4, 1)) {
- /* Unhandled */
- }
-#endif
-
- /*
- * Type 0x00d5: Unknown. (1 byte)
- */
- unknown_d5 = aim_tlv_get8(tlvlist, 0x00d5, 1);
-
-#if 0
- /*
- * Type 0x00d6: Encoding 1 ("us-ascii")
- */
- if (aim_tlv_gettlv(tlvlist, 0x000d6, 1)) {
- /* Unhandled */
- }
-
- /*
- * Type 0x00d7: Language 1 ("en")
- */
- if (aim_tlv_gettlv(tlvlist, 0x000d7, 1)) {
- /* Unhandled */
- }
-
- /*
- * Type 0x00d8: Encoding 2 ("us-ascii")
- */
- if (aim_tlv_gettlv(tlvlist, 0x000d8, 1)) {
- /* Unhandled */
- }
-
- /*
- * Type 0x00d9: Language 2 ("en")
- */
- if (aim_tlv_gettlv(tlvlist, 0x000d9, 1)) {
- /* Unhandled */
- }
-#endif
-
- /*
* Type 0x00da: Maximum visible message length
*/
maxvisiblemsglen = aim_tlv_get16(tlvlist, 0x00da, 1);
if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) {
- ret = userfunc(od, conn,
- frame,
- &roominfo,
- roomname,
- usercount,
- userinfo,
- roomdesc,
- flags,
- creationtime,
- maxmsglen,
- unknown_d2,
- unknown_d5,
- maxvisiblemsglen);
+ ret = userfunc(od, conn, frame, maxmsglen, maxvisiblemsglen);
}
g_free(roominfo.name);
- while (usercount > 0)
- aim_info_free(&userinfo[--usercount]);
-
- g_free(userinfo);
- g_free(roomname);
- g_free(roomdesc);
aim_tlvlist_free(tlvlist);
return ret;
@@ -324,7 +122,7 @@ userlistchange(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame
aim_rxcallback_t userfunc;
int curcount = 0, ret = 0;
- while (byte_stream_empty(bs)) {
+ while (byte_stream_bytes_left(bs)) {
curcount++;
userinfo = g_realloc(userinfo, curcount * sizeof(aim_userinfo_t));
aim_info_extract(od, bs, &userinfo[curcount-1]);
@@ -434,7 +232,7 @@ aim_chat_send_im(OscarData *od, FlapConnection *conn, guint16 flags, const gchar
aim_tlvlist_free(inner_tlvlist);
aim_tlvlist_free(tlvlist);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_CHAT, 0x0005, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_CHAT, 0x0005, snacid, &bs);
byte_stream_destroy(&bs);
@@ -523,16 +321,6 @@ incomingim_ch3(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame
aim_info_extract(od, &tbs, &userinfo);
}
-#if 0
- /*
- * Type 0x0001: If present, it means it was a message to the
- * room (as opposed to a whisper).
- */
- if (aim_tlv_gettlv(tlvlist, 0x0001, 1)) {
- /* Unhandled */
- }
-#endif
-
/*
* Type 0x0005: Message Block. Conains more TLVs.
*/
diff --git a/libpurple/protocols/oscar/family_chatnav.c b/libpurple/protocols/oscar/family_chatnav.c
index 1509b3c14e..6df24b2dde 100644
--- a/libpurple/protocols/oscar/family_chatnav.c
+++ b/libpurple/protocols/oscar/family_chatnav.c
@@ -37,7 +37,8 @@ error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame,
guint16 error, chatnav_error;
GSList *tlvlist;
- if (!(snac2 = aim_remsnac(od, snac->id))) {
+ snac2 = aim_remsnac(od, snac->id);
+ if (!snac2) {
purple_debug_warning("oscar", "chatnav error: received response to unknown request (%08x)\n", snac->id);
return 0;
}
@@ -67,8 +68,7 @@ error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame,
ret = 1;
}
- if (snac2)
- g_free(snac2->data);
+ g_free(snac2->data);
g_free(snac2);
return ret;
@@ -139,7 +139,7 @@ int aim_chatnav_createroom(OscarData *od, FlapConnection *conn, const char *name
aim_tlvlist_free(tlvlist);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_CHATNAV, 0x0008, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_CHATNAV, 0x0008, snacid, &bs);
byte_stream_destroy(&bs);
@@ -185,32 +185,6 @@ parseinfo_perms(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFram
exchanges[curexchange-1].number = byte_stream_get16(&tbs);
innerlist = aim_tlvlist_read(&tbs);
-#if 0
- /*
- * Type 0x000a: Unknown.
- *
- * Usually three bytes: 0x0114 (exchange 1) or 0x010f (others).
- *
- */
- if (aim_tlv_gettlv(innerlist, 0x000a, 1)) {
- /* Unhandled */
- }
-
- /*
- * Type 0x000d: Unknown.
- */
- if (aim_tlv_gettlv(innerlist, 0x000d, 1)) {
- /* Unhandled */
- }
-
- /*
- * Type 0x0004: Unknown
- */
- if (aim_tlv_gettlv(innerlist, 0x0004, 1)) {
- /* Unhandled */
- }
-#endif
-
/*
* Type 0x0002: Unknown
*/
@@ -234,36 +208,6 @@ parseinfo_perms(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFram
if (aim_tlv_gettlv(innerlist, 0x00c9, 1))
exchanges[curexchange-1].flags = aim_tlv_get16(innerlist, 0x00c9, 1);
-#if 0
- /*
- * Type 0x00ca: Creation Date
- */
- if (aim_tlv_gettlv(innerlist, 0x00ca, 1)) {
- /* Unhandled */
- }
-
- /*
- * Type 0x00d0: Mandatory Channels?
- */
- if (aim_tlv_gettlv(innerlist, 0x00d0, 1)) {
- /* Unhandled */
- }
-
- /*
- * Type 0x00d1: Maximum Message length
- */
- if (aim_tlv_gettlv(innerlist, 0x00d1, 1)) {
- /* Unhandled */
- }
-
- /*
- * Type 0x00d2: Maximum Occupancy?
- */
- if (aim_tlv_gettlv(innerlist, 0x00d2, 1)) {
- /* Unhandled */
- }
-#endif
-
/*
* Type 0x00d3: Exchange Description
*/
@@ -272,15 +216,6 @@ parseinfo_perms(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFram
else
exchanges[curexchange-1].name = NULL;
-#if 0
- /*
- * Type 0x00d4: Exchange Description URL
- */
- if (aim_tlv_gettlv(innerlist, 0x00d4, 1)) {
- /* Unhandled */
- }
-#endif
-
/*
* Type 0x00d5: Creation Permissions
*
@@ -327,15 +262,6 @@ parseinfo_perms(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFram
else
exchanges[curexchange-1].lang2 = NULL;
-#if 0
- /*
- * Type 0x00da: Unknown
- */
- if (aim_tlv_gettlv(innerlist, 0x00da, 1)) {
- /* Unhandled */
- }
-#endif
-
aim_tlvlist_free(innerlist);
}
diff --git a/libpurple/protocols/oscar/family_feedbag.c b/libpurple/protocols/oscar/family_feedbag.c
index 37e9518cd3..c685f51ec8 100644
--- a/libpurple/protocols/oscar/family_feedbag.c
+++ b/libpurple/protocols/oscar/family_feedbag.c
@@ -660,10 +660,8 @@ int aim_ssi_cleanlist(OscarData *od)
if (!cur->name) {
if (cur->type == AIM_SSI_TYPE_BUDDY)
aim_ssi_delbuddy(od, NULL, NULL);
- else if (cur->type == AIM_SSI_TYPE_PERMIT)
- aim_ssi_delpermit(od, NULL);
- else if (cur->type == AIM_SSI_TYPE_DENY)
- aim_ssi_deldeny(od, 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);
@@ -748,51 +746,31 @@ int aim_ssi_addbuddy(OscarData *od, const char *name, const char *group, GSList
return aim_ssi_sync(od);
}
-/**
- * Add a permit buddy to the list.
- *
- * @param od The oscar odion.
- * @param name The name of the item..
- * @return Return 0 if no errors, otherwise return the error number.
- */
-int aim_ssi_addpermit(OscarData *od, const char *name)
+int
+aim_ssi_add_to_private_list(OscarData *od, const char* name, guint16 list_type)
{
-
if (!od || !name || !od->ssi.received_data)
return -EINVAL;
- /* Make sure the master group exists */
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 that bad boy */
- aim_ssi_itemlist_add(&od->ssi.local, name, 0x0000, 0xFFFF, AIM_SSI_TYPE_PERMIT, NULL);
+ aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0x0000, list_type, NULL);
- /* Sync our local list with the server list */
+ aim_ssi_itemlist_add(&od->ssi.local, name, 0x0000, 0xFFFF, list_type, NULL);
return aim_ssi_sync(od);
}
-/**
- * Add a deny buddy to the list.
- *
- * @param od The oscar odion.
- * @param name The name of the item..
- * @return Return 0 if no errors, otherwise return the error number.
- */
-int aim_ssi_adddeny(OscarData *od, const char *name)
+int
+aim_ssi_del_from_private_list(OscarData* od, const char* name, guint16 list_type)
{
+ struct aim_ssi_item *del;
- if (!od || !name || !od->ssi.received_data)
+ if (!od)
return -EINVAL;
- /* Make sure the master group exists */
- 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 that bad boy */
- aim_ssi_itemlist_add(&od->ssi.local, name, 0x0000, 0xFFFF, AIM_SSI_TYPE_DENY, NULL);
+ if (!(del = aim_ssi_itemlist_finditem(od->ssi.local, NULL, name, list_type)))
+ return -EINVAL;
- /* Sync our local list with the server list */
+ aim_ssi_itemlist_del(&od->ssi.local, del);
return aim_ssi_sync(od);
}
@@ -860,56 +838,6 @@ int aim_ssi_delgroup(OscarData *od, const char *group)
}
/**
- * Deletes a permit buddy from the list.
- *
- * @param od The oscar odion.
- * @param name The name of the item, or NULL.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-int aim_ssi_delpermit(OscarData *od, const char *name)
-{
- struct aim_ssi_item *del;
-
- if (!od)
- return -EINVAL;
-
- /* Find the item */
- if (!(del = aim_ssi_itemlist_finditem(od->ssi.local, NULL, name, AIM_SSI_TYPE_PERMIT)))
- return -EINVAL;
-
- /* Remove the item from the list */
- aim_ssi_itemlist_del(&od->ssi.local, del);
-
- /* Sync our local list with the server list */
- return aim_ssi_sync(od);
-}
-
-/**
- * Deletes a deny buddy from the list.
- *
- * @param od The oscar odion.
- * @param name The name of the item, or NULL.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-int aim_ssi_deldeny(OscarData *od, const char *name)
-{
- struct aim_ssi_item *del;
-
- if (!od)
- return -EINVAL;
-
- /* Find the item */
- if (!(del = aim_ssi_itemlist_finditem(od->ssi.local, NULL, name, AIM_SSI_TYPE_DENY)))
- return -EINVAL;
-
- /* Remove the item from the list */
- aim_ssi_itemlist_del(&od->ssi.local, del);
-
- /* Sync our local list with the server list */
- return aim_ssi_sync(od);
-}
-
-/**
* Move a buddy from one group to another group. This basically just deletes the
* buddy and re-adds it.
*
@@ -1030,17 +958,16 @@ int aim_ssi_rename_group(OscarData *od, const char *oldgn, const char *newgn)
* Stores your permit/deny setting on the server, and starts using it.
*
* @param od The oscar odion.
- * @param permdeny Your permit/deny setting. Can be one of the following:
+ * @param permdeny Your permit/deny setting. For ICQ accounts, it actually affects your visibility
+ * and has nothing to do with blocking. Can be one of the following:
* 1 - Allow all users
* 2 - Block all users
* 3 - Allow only the users below
* 4 - Block only the users below
* 5 - Allow only users on my buddy list
- * @param vismask A bitmask of the class of users to whom you want to be
- * visible. See the AIM_FLAG_BLEH #defines in oscar.h
* @return Return 0 if no errors, otherwise return the error number.
*/
-int aim_ssi_setpermdeny(OscarData *od, guint8 permdeny, guint32 vismask)
+int aim_ssi_setpermdeny(OscarData *od, guint8 permdeny)
{
struct aim_ssi_item *tmp;
@@ -1059,9 +986,6 @@ int aim_ssi_setpermdeny(OscarData *od, guint8 permdeny, guint32 vismask)
/* Need to add the 0x00ca TLV to the TLV chain */
aim_tlvlist_replace_8(&tmp->data, 0x00ca, permdeny);
- /* Need to add the 0x00cb TLV to the TLV chain */
- aim_tlvlist_replace_32(&tmp->data, 0x00cb, vismask);
-
/* Sync our local list with the server list */
return aim_ssi_sync(od);
}
@@ -1231,41 +1155,6 @@ int aim_ssi_reqdata(OscarData *od)
}
/*
- * Subtype 0x0005 - Request SSI Data when you have a timestamp and revision
- * number.
- *
- * The data will only be sent if it is newer than the posted local
- * timestamp and revision.
- *
- * Note that the client should never increment the revision, only the server.
- *
- */
-int aim_ssi_reqifchanged(OscarData *od, time_t timestamp, guint16 numitems)
-{
- FlapConnection *conn;
- ByteStream bs;
- aim_snacid_t snacid;
-
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)))
- return -EINVAL;
-
- byte_stream_new(&bs, 4+2);
-
- byte_stream_put32(&bs, timestamp);
- byte_stream_put16(&bs, numitems);
-
- snacid = aim_cachesnac(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_REQIFCHANGED, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_REQIFCHANGED, 0x0000, snacid, &bs);
-
- byte_stream_destroy(&bs);
-
- /* Free any current data, just in case */
- aim_ssi_freelist(od);
-
- return 0;
-}
-
-/*
* Subtype 0x0006 - SSI Data.
*/
static int parsedata(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
@@ -1281,7 +1170,7 @@ static int parsedata(OscarData *od, FlapConnection *conn, aim_module_t *mod, Fla
od->ssi.numitems += byte_stream_get16(bs); /* # of items in this SSI SNAC */
/* Read in the list */
- while (byte_stream_empty(bs) > 4) { /* last four bytes are timestamp */
+ while (byte_stream_bytes_left(bs) > 4) { /* last four bytes are timestamp */
if ((namelen = byte_stream_get16(bs)))
name = byte_stream_getstr(bs, namelen);
else
@@ -1378,7 +1267,7 @@ static int aim_ssi_addmoddel(OscarData *od)
}
snacid = aim_cachesnac(od, SNAC_FAMILY_FEEDBAG, od->ssi.pending->action, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_FEEDBAG, od->ssi.pending->action, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_FEEDBAG, od->ssi.pending->action, snacid, &bs);
byte_stream_destroy(&bs);
@@ -1399,7 +1288,7 @@ static int parseadd(OscarData *od, FlapConnection *conn, aim_module_t *mod, Flap
guint16 len, gid, bid, type;
GSList *data;
- while (byte_stream_empty(bs)) {
+ while (byte_stream_bytes_left(bs)) {
if ((len = byte_stream_get16(bs)))
name = byte_stream_getstr(bs, len);
else
@@ -1437,7 +1326,7 @@ static int parsemod(OscarData *od, FlapConnection *conn, aim_module_t *mod, Flap
GSList *data;
struct aim_ssi_item *item;
- while (byte_stream_empty(bs)) {
+ while (byte_stream_bytes_left(bs)) {
if ((len = byte_stream_get16(bs)))
name = byte_stream_getstr(bs, len);
else
@@ -1489,7 +1378,7 @@ static int parsedel(OscarData *od, FlapConnection *conn, aim_module_t *mod, Flap
guint16 gid, bid;
struct aim_ssi_item *del;
- while (byte_stream_empty(bs)) {
+ while (byte_stream_bytes_left(bs)) {
byte_stream_advance(bs, byte_stream_get16(bs));
gid = byte_stream_get16(bs);
bid = byte_stream_get16(bs);
@@ -1522,7 +1411,7 @@ static int parseack(OscarData *od, FlapConnection *conn, aim_module_t *mod, Flap
/* Read in the success/failure flags from the ack SNAC */
cur = od->ssi.pending;
- while (cur && (byte_stream_empty(bs)>0)) {
+ while (cur && (byte_stream_bytes_left(bs)>0)) {
cur->ack = byte_stream_get16(bs);
cur = cur->next;
}
@@ -1678,45 +1567,6 @@ int aim_ssi_modend(OscarData *od)
}
/*
- * Subtype 0x0014 - Grant authorization
- *
- * Authorizes a contact so they can add you to their contact list.
- *
- */
-int aim_ssi_sendauth(OscarData *od, char *bn, char *msg)
-{
- FlapConnection *conn;
- ByteStream bs;
- aim_snacid_t snacid;
-
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)) || !bn)
- return -EINVAL;
-
- byte_stream_new(&bs, 1+strlen(bn) + 2+(msg ? strlen(msg)+1 : 0) + 2);
-
- /* Username */
- byte_stream_put8(&bs, strlen(bn));
- byte_stream_putstr(&bs, bn);
-
- /* Message (null terminated) */
- byte_stream_put16(&bs, msg ? strlen(msg) : 0);
- if (msg) {
- byte_stream_putstr(&bs, msg);
- byte_stream_put8(&bs, 0x00);
- }
-
- /* Unknown */
- byte_stream_put16(&bs, 0x0000);
-
- snacid = aim_cachesnac(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SENDAUTH, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SENDAUTH, 0x0000, snacid, &bs);
-
- byte_stream_destroy(&bs);
-
- return 0;
-}
-
-/*
* Subtype 0x0015 - Receive an authorization grant
*/
static int receiveauthgrant(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
@@ -1783,7 +1633,7 @@ int aim_ssi_sendauthrequest(OscarData *od, char *bn, const char *msg)
byte_stream_put16(&bs, 0x0000);
snacid = aim_cachesnac(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SENDAUTHREQ, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SENDAUTHREQ, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SENDAUTHREQ, snacid, &bs);
byte_stream_destroy(&bs);
@@ -1863,7 +1713,7 @@ int aim_ssi_sendauthreply(OscarData *od, char *bn, guint8 reply, const char *msg
byte_stream_put16(&bs, 0x0000);
snacid = aim_cachesnac(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SENDAUTHREP, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SENDAUTHREP, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SENDAUTHREP, snacid, &bs);
byte_stream_destroy(&bs);
@@ -1935,6 +1785,16 @@ static int receiveadded(OscarData *od, FlapConnection *conn, aim_module_t *mod,
return ret;
}
+/*
+ * If we're on ICQ, then AIM_SSI_TYPE_DENY is used for the "permanently invisible" list.
+ * AIM_SSI_TYPE_ICQDENY is used for blocking users instead.
+ */
+guint16
+aim_ssi_getdenyentrytype(OscarData* od)
+{
+ return od->icq ? AIM_SSI_TYPE_ICQDENY : AIM_SSI_TYPE_DENY;
+}
+
static int
snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
{
diff --git a/libpurple/protocols/oscar/family_icbm.c b/libpurple/protocols/oscar/family_icbm.c
index a01a6e56f1..73a3aa7862 100644
--- a/libpurple/protocols/oscar/family_icbm.c
+++ b/libpurple/protocols/oscar/family_icbm.c
@@ -44,6 +44,7 @@
* Make sure flap_connection_findbygroup is used by all functions.
*/
+#include "encoding.h"
#include "oscar.h"
#include "peer.h"
@@ -53,6 +54,25 @@
#include "util.h"
+static const char * const errcodereason[] = {
+ N_("Invalid error"),
+ N_("Not logged in"),
+ N_("Cannot receive IM due to parental controls"),
+ N_("Cannot send SMS without accepting terms"),
+ N_("Cannot send SMS"), /* SMS_WITHOUT_DISCLAIMER is weird */
+ N_("Cannot send SMS to this country"),
+ N_("Unknown error"), /* Undocumented */
+ N_("Unknown error"), /* Undocumented */
+ N_("Cannot send SMS to unknown country"),
+ N_("Bot accounts cannot initiate IMs"),
+ N_("Bot account cannot IM this user"),
+ N_("Bot account reached IM limit"),
+ N_("Bot account reached daily IM limit"),
+ N_("Bot account reached monthly IM limit"),
+ N_("Unable to receive offline messages"),
+ N_("Offline message store full")
+};
+static const int errcodereasonlen = G_N_ELEMENTS(errcodereason);
/**
* Add a standard ICBM header to the given bstream with the given
@@ -89,97 +109,42 @@ void aim_icbm_makecookie(guchar *cookie)
}
/*
- * Takes a msghdr (and a length) and returns a client type
- * code. Note that this is *only a guess* and has a low likelihood
- * of actually being accurate.
- *
- * Its based on experimental data, with the help of Eric Warmenhoven
- * who seems to have collected a wide variety of different AIM clients.
- *
- *
- * Heres the current collection:
- * 0501 0003 0101 0101 01 AOL Mobile Communicator, WinAIM 1.0.414
- * 0501 0003 0101 0201 01 WinAIM 2.0.847, 2.1.1187, 3.0.1464,
- * 4.3.2229, 4.4.2286
- * 0501 0004 0101 0102 0101 WinAIM 4.1.2010, libfaim (right here)
- * 0501 0003 0101 02 WinAIM 5
- * 0501 0001 01 iChat x.x, mobile buddies
- * 0501 0001 0101 01 AOL v6.0, CompuServe 2000 v6.0, any TOC client
- * 0501 0002 0106 WinICQ 5.45.1.3777.85
- *
- * Note that in this function, only the feature bytes are tested, since
- * the rest will always be the same.
- *
- */
-guint16 aim_im_fingerprint(const guint8 *msghdr, int len)
-{
- static const struct {
- guint16 clientid;
- int len;
- guint8 data[10];
- } fingerprints[] = {
- /* AOL Mobile Communicator, WinAIM 1.0.414 */
- { AIM_CLIENTTYPE_MC,
- 3, {0x01, 0x01, 0x01}},
-
- /* WinAIM 2.0.847, 2.1.1187, 3.0.1464, 4.3.2229, 4.4.2286 */
- { AIM_CLIENTTYPE_WINAIM,
- 3, {0x01, 0x01, 0x02}},
-
- /* WinAIM 4.1.2010, libfaim */
- { AIM_CLIENTTYPE_WINAIM41,
- 4, {0x01, 0x01, 0x01, 0x02}},
-
- /* AOL v6.0, CompuServe 2000 v6.0, any TOC client */
- { AIM_CLIENTTYPE_AOL_TOC,
- 1, {0x01}},
-
- { 0, 0, {0x00}}
- };
- int i;
-
- if (!msghdr || (len <= 0))
- return AIM_CLIENTTYPE_UNKNOWN;
-
- for (i = 0; fingerprints[i].len; i++) {
- if (fingerprints[i].len != len)
- continue;
- if (memcmp(fingerprints[i].data, msghdr, fingerprints[i].len) == 0)
- return fingerprints[i].clientid;
- }
-
- return AIM_CLIENTTYPE_UNKNOWN;
-}
-
-/*
* Subtype 0x0001 - Error
*/
static int
error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
{
- int ret = 0;
- aim_rxcallback_t userfunc;
aim_snac_t *snac2;
guint16 reason, errcode = 0;
- char *bn;
+ const char *bn;
GSList *tlvlist;
+ PurpleConnection *gc = od->gc;
+#ifdef TODOFT
+ PurpleXfer *xfer;
+#endif
+ const char *reason_str;
+ char *buf;
- if (!(snac2 = aim_remsnac(od, snac->id))) {
+ snac2 = aim_remsnac(od, snac->id);
+ if (!snac2) {
purple_debug_misc("oscar", "icbm error: received response from unknown request!\n");
- return 0;
+ return 1;
}
if (snac2->family != SNAC_FAMILY_ICBM) {
purple_debug_misc("oscar", "icbm error: received response from invalid request! %d\n", snac2->family);
g_free(snac2->data);
g_free(snac2);
- return 0;
+ return 1;
}
- if (!(bn = snac2->data)) {
+ /* Data is assumed to be the destination bn */
+ bn = snac2->data;
+ if (!bn || bn[0] == '\0') {
purple_debug_misc("oscar", "icbm error: received response from request without a buddy name!\n");
+ g_free(snac2->data);
g_free(snac2);
- return 0;
+ return 1;
}
reason = byte_stream_get16(bs);
@@ -189,15 +154,43 @@ error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame,
errcode = aim_tlv_get16(tlvlist, 0x0008, 1);
aim_tlvlist_free(tlvlist);
+ purple_debug_error("oscar",
+ "Message error with bn %s and reason %hu and errcode %hu\n",
+ (bn != NULL ? bn : ""), reason, errcode);
+
+#ifdef TODOFT
+ /* If this was a file transfer request, bn is a cookie */
+ if ((xfer = oscar_find_xfer_by_cookie(od->file_transfers, bn))) {
+ purple_xfer_cancel_remote(xfer);
+ return 1;
+ }
+#endif
+
/* Notify the user that the message wasn't delivered */
- if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
- ret = userfunc(od, conn, frame, reason, errcode, bn);
+ reason_str = oscar_get_msgerr_reason(reason);
+ if (errcode != 0 && errcode < errcodereasonlen)
+ buf = g_strdup_printf(_("Unable to send message: %s (%s)"), reason_str,
+ _(errcodereason[errcode]));
+ else
+ buf = g_strdup_printf(_("Unable to send message: %s"), reason_str);
- if (snac2)
- g_free(snac2->data);
+ if (!purple_conv_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)"),
+ bn ? bn : "(unknown)", reason_str,
+ _(errcodereason[errcode]));
+ 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);
+ }
+ g_free(buf);
+
+ g_free(snac2->data);
g_free(snac2);
- return ret;
+ return 1;
}
/**
@@ -232,7 +225,7 @@ int aim_im_setparams(OscarData *od, struct aim_icbmparameters *params)
byte_stream_put32(&bs, params->minmsginterval);
snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0002, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0002, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0002, snacid, &bs);
byte_stream_destroy(&bs);
@@ -274,7 +267,8 @@ static int aim_im_paraminfo(OscarData *od, FlapConnection *conn, aim_module_t *m
| AIM_IMPARAM_FLAG_MISSED_CALLS_ENABLED
| AIM_IMPARAM_FLAG_EVENTS_ALLOWED
| AIM_IMPARAM_FLAG_SMS_SUPPORTED
- | AIM_IMPARAM_FLAG_OFFLINE_MSGS_ALLOWED;
+ | AIM_IMPARAM_FLAG_OFFLINE_MSGS_ALLOWED
+ | AIM_IMPARAM_FLAG_USE_HTML_FOR_ICQ;
params.maxmsglen = 8000;
params.minmsginterval = 0;
@@ -289,40 +283,14 @@ static int aim_im_paraminfo(OscarData *od, FlapConnection *conn, aim_module_t *m
*
* Possible flags:
* AIM_IMFLAGS_AWAY -- Marks the message as an autoresponse
- * AIM_IMFLAGS_ACK -- Requests that the server send an ack
- * when the message is received (of type SNAC_FAMILY_ICBM/0x000c)
* AIM_IMFLAGS_OFFLINE--If destination is offline, store it until they are
* online (probably ICQ only).
*
- * Generally, you should use the lowest encoding possible to send
- * your message. If you only use basic punctuation and the generic
- * Latin alphabet, use ASCII7 (no flags). If you happen to use non-ASCII7
- * characters, but they are all clearly defined in ISO-8859-1, then
- * use that. Keep in mind that not all characters in the PC ASCII8
- * character set are defined in the ISO standard. For those cases (most
- * notably when the (r) symbol is used), you must use the full UNICODE
- * encoding for your message. In UNICODE mode, _all_ characters must
- * occupy 16bits, including ones that are not special. (Remember that
- * the first 128 UNICODE symbols are equivalent to ASCII7, however they
- * must be prefixed with a zero high order byte.)
- *
- * I strongly discourage the use of UNICODE mode, mainly because none
- * of the clients I use can parse those messages (and besides that,
- * wchars are difficult and non-portable to handle in most UNIX environments).
- * If you really need to include special characters, use the HTML UNICODE
- * entities. These are of the form &#2026; where 2026 is the hex
- * representation of the UNICODE index (in this case, UNICODE
- * "Horizontal Ellipsis", or 133 in in ASCII8).
- *
* Implementation note: Since this is one of the most-used functions
* in all of libfaim, it is written with performance in mind. As such,
* it is not as clear as it could be in respect to how this message is
* supposed to be layed out. Most obviously, tlvlists should be used
* instead of writing out the bytes manually.
- *
- * XXX - more precise verification that we never send SNACs larger than 8192
- * XXX - check SNAC size for multipart
- *
*/
int aim_im_sendch1_ext(OscarData *od, struct aim_sendimext_args *args)
{
@@ -331,7 +299,6 @@ int aim_im_sendch1_ext(OscarData *od, struct aim_sendimext_args *args)
ByteStream data;
guchar cookie[8];
int msgtlvlen;
- static const guint8 deffeatures[] = { 0x01, 0x01, 0x01, 0x02 };
if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
return -EINVAL;
@@ -339,37 +306,17 @@ int aim_im_sendch1_ext(OscarData *od, struct aim_sendimext_args *args)
if (!args)
return -EINVAL;
- if (args->flags & AIM_IMFLAGS_MULTIPART) {
- if (args->mpmsg->numparts == 0)
- return -EINVAL;
- } else {
- if (!args->msg || (args->msglen <= 0))
- return -EINVAL;
+ if (!args->msg || (args->msglen <= 0))
+ return -EINVAL;
- if (args->msglen > MAXMSGLEN)
- return -E2BIG;
- }
+ if (args->msglen > MAXMSGLEN)
+ return -E2BIG;
/* Painfully calculate the size of the message TLV */
msgtlvlen = 1 + 1; /* 0501 */
-
- if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES)
- msgtlvlen += 2 + args->featureslen;
- else
- msgtlvlen += 2 + sizeof(deffeatures);
-
- if (args->flags & AIM_IMFLAGS_MULTIPART) {
- aim_mpmsg_section_t *sec;
-
- for (sec = args->mpmsg->parts; sec; sec = sec->next) {
- msgtlvlen += 2 /* 0101 */ + 2 /* block len */;
- msgtlvlen += 4 /* charset */ + sec->datalen;
- }
-
- } else {
- msgtlvlen += 2 /* 0101 */ + 2 /* block len */;
- msgtlvlen += 4 /* charset */ + args->msglen;
- }
+ msgtlvlen += 2 + args->featureslen;
+ msgtlvlen += 2 /* 0101 */ + 2 /* block len */;
+ msgtlvlen += 4 /* charset */ + args->msglen;
byte_stream_new(&data, msgtlvlen + 128);
@@ -385,52 +332,31 @@ int aim_im_sendch1_ext(OscarData *od, struct aim_sendimext_args *args)
/* Features TLV (type 0x0501) */
byte_stream_put16(&data, 0x0501);
- if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES) {
- byte_stream_put16(&data, args->featureslen);
- byte_stream_putraw(&data, args->features, args->featureslen);
- } else {
- byte_stream_put16(&data, sizeof(deffeatures));
- byte_stream_putraw(&data, deffeatures, sizeof(deffeatures));
- }
-
- if (args->flags & AIM_IMFLAGS_MULTIPART) {
- aim_mpmsg_section_t *sec;
-
- /* Insert each message part in a TLV (type 0x0101) */
- for (sec = args->mpmsg->parts; sec; sec = sec->next) {
- byte_stream_put16(&data, 0x0101);
- byte_stream_put16(&data, sec->datalen + 4);
- byte_stream_put16(&data, sec->charset);
- byte_stream_put16(&data, sec->charsubset);
- byte_stream_putraw(&data, (guchar *)sec->data, sec->datalen);
- }
-
- } else {
+ byte_stream_put16(&data, args->featureslen);
+ byte_stream_putraw(&data, args->features, args->featureslen);
- /* Insert message text in a TLV (type 0x0101) */
- byte_stream_put16(&data, 0x0101);
+ /* Insert message text in a TLV (type 0x0101) */
+ byte_stream_put16(&data, 0x0101);
- /* Message block length */
- byte_stream_put16(&data, args->msglen + 0x04);
+ /* Message block length */
+ byte_stream_put16(&data, args->msglen + 0x04);
- /* Character set */
- byte_stream_put16(&data, args->charset);
- byte_stream_put16(&data, args->charsubset);
+ /* Character set */
+ byte_stream_put16(&data, args->charset);
+ /* Character subset -- we always use 0 here */
+ byte_stream_put16(&data, 0x0);
- /* Message. Not terminated */
- byte_stream_putraw(&data, (guchar *)args->msg, args->msglen);
- }
+ /* Message. Not terminated */
+ byte_stream_putraw(&data, (guchar *)args->msg, args->msglen);
/* Set the Autoresponse flag */
if (args->flags & AIM_IMFLAGS_AWAY) {
byte_stream_put16(&data, 0x0004);
byte_stream_put16(&data, 0x0000);
} else {
- if (args->flags & AIM_IMFLAGS_ACK) {
- /* Set the Request Acknowledge flag */
- byte_stream_put16(&data, 0x0003);
- byte_stream_put16(&data, 0x0000);
- }
+ /* Set the Request Acknowledge flag */
+ byte_stream_put16(&data, 0x0003);
+ byte_stream_put16(&data, 0x0000);
if (args->flags & AIM_IMFLAGS_OFFLINE) {
/* Allow this message to be queued as an offline message */
@@ -465,7 +391,7 @@ int aim_im_sendch1_ext(OscarData *od, struct aim_sendimext_args *args)
/* XXX - should be optional */
snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, args->destbn, strlen(args->destbn)+1);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &data);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &data);
byte_stream_destroy(&data);
/* clean out SNACs over 60sec old */
@@ -475,33 +401,6 @@ int aim_im_sendch1_ext(OscarData *od, struct aim_sendimext_args *args)
}
/*
- * Simple wrapper for aim_im_sendch1_ext()
- *
- * You cannot use aim_send_im if you need the HASICON flag. You must
- * use aim_im_sendch1_ext directly for that.
- *
- * aim_send_im also cannot be used if you require UNICODE messages, because
- * that requires an explicit message length. Use aim_im_sendch1_ext().
- *
- */
-int aim_im_sendch1(OscarData *od, const char *bn, guint16 flags, const char *msg)
-{
- struct aim_sendimext_args args;
-
- args.destbn = bn;
- args.flags = flags;
- args.msg = msg;
- args.msglen = strlen(msg);
- args.charset = 0x0000;
- args.charsubset = 0x0000;
-
- /* Make these don't get set by accident -- they need aim_im_sendch1_ext */
- args.flags &= ~(AIM_IMFLAGS_CUSTOMFEATURES | AIM_IMFLAGS_HASICON | AIM_IMFLAGS_MULTIPART);
-
- return aim_im_sendch1_ext(od, &args);
-}
-
-/*
* Subtype 0x0006 - Send a chat invitation.
*/
int aim_im_sendch2_chatinvite(OscarData *od, const char *bn, const char *msg, guint16 exchange, const char *roomname, guint16 instance)
@@ -572,7 +471,7 @@ int aim_im_sendch2_chatinvite(OscarData *od, const char *bn, const char *msg, gu
aim_tlvlist_free(inner_tlvlist);
aim_tlvlist_free(outer_tlvlist);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
byte_stream_destroy(&bs);
@@ -642,100 +541,7 @@ int aim_im_sendch2_icon(OscarData *od, const char *bn, const guint8 *icon, int i
byte_stream_put16(&bs, 0x0003);
byte_stream_put16(&bs, 0x0000);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
-
- byte_stream_destroy(&bs);
-
- return 0;
-}
-
-/*
- * Subtype 0x0006 - Send a rich text message.
- *
- * This only works for ICQ 2001b (thats 2001 not 2000). Better, only
- * send it to clients advertising the RTF capability. In fact, if you send
- * it to a client that doesn't support that capability, the server will gladly
- * bounce it back to you.
- *
- * You'd think this would be in icq.c, but, well, I'm trying to stick with
- * the one-group-per-file scheme as much as possible. This could easily
- * be an exception, since Rendezvous IMs are external of the Oscar core,
- * and therefore are undefined. Really I just need to think of a good way to
- * make an interface similar to what AOL actually uses. But I'm not using COM.
- *
- */
-int aim_im_sendch2_rtfmsg(OscarData *od, struct aim_sendrtfmsg_args *args)
-{
- FlapConnection *conn;
- ByteStream bs;
- aim_snacid_t snacid;
- guchar cookie[8];
- const char rtfcap[] = {"{97B12751-243C-4334-AD22-D6ABF73F1492}"}; /* OSCAR_CAPABILITY_ICQRTF capability in string form */
- int servdatalen;
-
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
- return -EINVAL;
-
- if (!args || !args->destbn || !args->rtfmsg)
- return -EINVAL;
-
- servdatalen = 2+2+16+2+4+1+2 + 2+2+4+4+4 + 2+4+2+strlen(args->rtfmsg)+1 + 4+4+4+strlen(rtfcap)+1;
-
- aim_icbm_makecookie(cookie);
-
- byte_stream_new(&bs, 128+servdatalen);
-
- snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
-
- /* ICBM header */
- aim_im_puticbm(&bs, cookie, 0x0002, args->destbn);
-
- /* TLV t(0005) - Encompasses everything below. */
- byte_stream_put16(&bs, 0x0005);
- byte_stream_put16(&bs, 2+8+16 + 2+2+2 + 2+2 + 2+2+servdatalen);
-
- byte_stream_put16(&bs, 0x0000);
- byte_stream_putraw(&bs, cookie, 8);
- byte_stream_putcaps(&bs, OSCAR_CAPABILITY_ICQSERVERRELAY);
-
- /* t(000a) l(0002) v(0001) */
- byte_stream_put16(&bs, 0x000a);
- byte_stream_put16(&bs, 0x0002);
- byte_stream_put16(&bs, 0x0001);
-
- /* t(000f) l(0000) v() */
- byte_stream_put16(&bs, 0x000f);
- byte_stream_put16(&bs, 0x0000);
-
- /* Service Data TLV */
- byte_stream_put16(&bs, 0x2711);
- byte_stream_put16(&bs, servdatalen);
-
- byte_stream_putle16(&bs, 11 + 16 /* 11 + (sizeof CLSID) */);
- byte_stream_putle16(&bs, 9);
- byte_stream_putcaps(&bs, OSCAR_CAPABILITY_EMPTY);
- byte_stream_putle16(&bs, 0);
- byte_stream_putle32(&bs, 0);
- byte_stream_putle8(&bs, 0);
- byte_stream_putle16(&bs, 0x03ea); /* trid1 */
-
- byte_stream_putle16(&bs, 14);
- byte_stream_putle16(&bs, 0x03eb); /* trid2 */
- byte_stream_putle32(&bs, 0);
- byte_stream_putle32(&bs, 0);
- byte_stream_putle32(&bs, 0);
-
- byte_stream_putle16(&bs, 0x0001);
- byte_stream_putle32(&bs, 0);
- byte_stream_putle16(&bs, strlen(args->rtfmsg)+1);
- byte_stream_putraw(&bs, (const guint8 *)args->rtfmsg, strlen(args->rtfmsg)+1);
-
- byte_stream_putle32(&bs, args->fgcolor);
- byte_stream_putle32(&bs, args->bgcolor);
- byte_stream_putle32(&bs, strlen(rtfcap)+1);
- byte_stream_putraw(&bs, (const guint8 *)rtfcap, strlen(rtfcap)+1);
-
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
byte_stream_destroy(&bs);
@@ -788,7 +594,7 @@ aim_im_sendch2_cancel(PeerConnection *peer_conn)
aim_tlvlist_free(inner_tlvlist);
aim_tlvlist_free(outer_tlvlist);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -823,7 +629,7 @@ aim_im_sendch2_connected(PeerConnection *peer_conn)
byte_stream_putraw(&bs, peer_conn->cookie, 8);
byte_stream_putcaps(&bs, peer_conn->type);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -878,7 +684,7 @@ aim_im_sendch2_odc_requestdirect(OscarData *od, guchar *cookie, const char *bn,
aim_tlvlist_free(inner_tlvlist);
aim_tlvlist_free(outer_tlvlist);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -941,7 +747,7 @@ aim_im_sendch2_odc_requestproxy(OscarData *od, guchar *cookie, const char *bn, c
aim_tlvlist_free(inner_tlvlist);
aim_tlvlist_free(outer_tlvlist);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -988,17 +794,6 @@ aim_im_sendch2_sendfile_requestdirect(OscarData *od, guchar *cookie, const char
aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
/* TODO: Send 0x0016 and 0x0017 */
-#if 0
- /* TODO: If the following is ever enabled, ensure that it is
- * not sent with a receive redirect or stage 3 proxy
- * redirect for a file receive (same conditions for
- * sending 0x000f above)
- */
- aim_tlvlist_add_raw(&inner_tlvlist, 0x000e, 2, "en");
- aim_tlvlist_add_raw(&inner_tlvlist, 0x000d, 8, "us-ascii");
- aim_tlvlist_add_raw(&inner_tlvlist, 0x000c, 24, "Please accept this file.");
-#endif
-
if (filename != NULL)
{
ByteStream inner_bs;
@@ -1027,7 +822,7 @@ aim_im_sendch2_sendfile_requestdirect(OscarData *od, guchar *cookie, const char
aim_tlvlist_free(inner_tlvlist);
aim_tlvlist_free(outer_tlvlist);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -1080,17 +875,6 @@ aim_im_sendch2_sendfile_requestproxy(OscarData *od, guchar *cookie, const char *
aim_tlvlist_add_raw(&inner_tlvlist, 0x0016, 4, ip_comp);
aim_tlvlist_add_16(&inner_tlvlist, 0x0017, ~pin);
-#if 0
- /* TODO: If the following is ever enabled, ensure that it is
- * not sent with a receive redirect or stage 3 proxy
- * redirect for a file receive (same conditions for
- * sending 0x000f above)
- */
- aim_tlvlist_add_raw(&inner_tlvlist, 0x000e, 2, "en");
- aim_tlvlist_add_raw(&inner_tlvlist, 0x000d, 8, "us-ascii");
- aim_tlvlist_add_raw(&inner_tlvlist, 0x000c, 24, "Please accept this file.");
-#endif
-
if (filename != NULL)
{
ByteStream filename_bs;
@@ -1120,530 +904,57 @@ aim_im_sendch2_sendfile_requestproxy(OscarData *od, guchar *cookie, const char *
aim_tlvlist_free(inner_tlvlist);
aim_tlvlist_free(outer_tlvlist);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
byte_stream_destroy(&bs);
}
-/**
- * Subtype 0x0006 - Request the status message of the given ICQ user.
- *
- * @param od The oscar session.
- * @param bn The UIN of the user of whom you wish to request info.
- * @param type The type of info you wish to request. This should be the current
- * state of the user, as one of the AIM_ICQ_STATE_* defines.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-int aim_im_sendch2_geticqaway(OscarData *od, const char *bn, int type)
-{
- FlapConnection *conn;
- ByteStream bs;
- aim_snacid_t snacid;
- guchar cookie[8];
-
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)) || !bn)
- return -EINVAL;
-
- aim_icbm_makecookie(cookie);
-
- byte_stream_new(&bs, 8+2+1+strlen(bn) + 4+0x5e + 4);
-
- snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
-
- /* ICBM header */
- aim_im_puticbm(&bs, cookie, 0x0002, bn);
-
- /* TLV t(0005) - Encompasses almost everything below. */
- byte_stream_put16(&bs, 0x0005); /* T */
- byte_stream_put16(&bs, 0x005e); /* L */
- { /* V */
- byte_stream_put16(&bs, 0x0000);
-
- /* Cookie */
- byte_stream_putraw(&bs, cookie, 8);
-
- /* Put the 16 byte server relay capability */
- byte_stream_putcaps(&bs, OSCAR_CAPABILITY_ICQSERVERRELAY);
-
- /* TLV t(000a) */
- byte_stream_put16(&bs, 0x000a);
- byte_stream_put16(&bs, 0x0002);
- byte_stream_put16(&bs, 0x0001);
-
- /* TLV t(000f) */
- byte_stream_put16(&bs, 0x000f);
- byte_stream_put16(&bs, 0x0000);
-
- /* TLV t(2711) */
- byte_stream_put16(&bs, 0x2711);
- byte_stream_put16(&bs, 0x0036);
- { /* V */
- byte_stream_putle16(&bs, 0x001b); /* L */
- byte_stream_putle16(&bs, 0x0009); /* Protocol version */
- byte_stream_putcaps(&bs, OSCAR_CAPABILITY_EMPTY);
- byte_stream_putle16(&bs, 0x0000); /* Unknown */
- byte_stream_putle16(&bs, 0x0001); /* Client features? */
- byte_stream_putle16(&bs, 0x0000); /* Unknown */
- byte_stream_putle8(&bs, 0x00); /* Unkizown */
- byte_stream_putle16(&bs, 0xffff); /* Sequence number? XXX - This should decrement by 1 with each request */
-
- byte_stream_putle16(&bs, 0x000e); /* L */
- byte_stream_putle16(&bs, 0xffff); /* Sequence number? XXX - This should decrement by 1 with each request */
- byte_stream_putle32(&bs, 0x00000000); /* Unknown */
- byte_stream_putle32(&bs, 0x00000000); /* Unknown */
- byte_stream_putle32(&bs, 0x00000000); /* Unknown */
-
- /* The type of status message being requested */
- if (type & AIM_ICQ_STATE_CHAT)
- byte_stream_putle16(&bs, 0x03ec);
- else if(type & AIM_ICQ_STATE_DND)
- byte_stream_putle16(&bs, 0x03eb);
- else if(type & AIM_ICQ_STATE_OUT)
- byte_stream_putle16(&bs, 0x03ea);
- else if(type & AIM_ICQ_STATE_BUSY)
- byte_stream_putle16(&bs, 0x03e9);
- else if(type & AIM_ICQ_STATE_AWAY)
- byte_stream_putle16(&bs, 0x03e8);
-
- byte_stream_putle16(&bs, 0x0001); /* Status? */
- byte_stream_putle16(&bs, 0x0001); /* Priority of this message? */
- byte_stream_putle16(&bs, 0x0001); /* L */
- byte_stream_putle8(&bs, 0x00); /* String of length L */
- } /* End TLV t(2711) */
- } /* End TLV t(0005) */
-
- /* TLV t(0003) */
- byte_stream_put16(&bs, 0x0003);
- byte_stream_put16(&bs, 0x0000);
-
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
-
- byte_stream_destroy(&bs);
-
- return 0;
-}
-
-/**
- * Subtype 0x0006 - Send an ICQ-esque ICBM.
- *
- * This can be used to send an ICQ authorization reply (deny or grant). It is the "old way."
- * The new way is to use SSI. I like the new way a lot better. This seems like such a hack,
- * mostly because it's in network byte order. Figuring this stuff out sometimes takes a while,
- * but thats ok, because it gives me time to try to figure out what kind of drugs the AOL people
- * were taking when they merged the two protocols.
- *
- * @param bn The destination buddy name.
- * @param type The type of message. 0x0007 for authorization denied. 0x0008 for authorization granted.
- * @param message The message you want to send, it should be null terminated.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-int aim_im_sendch4(OscarData *od, const char *bn, guint16 type, const char *message)
-{
- FlapConnection *conn;
- ByteStream bs;
- aim_snacid_t snacid;
- guchar cookie[8];
-
- if (!od || !(conn = flap_connection_findbygroup(od, 0x0002)))
- return -EINVAL;
-
- if (!bn || !type || !message)
- return -EINVAL;
-
- byte_stream_new(&bs, 8+3+strlen(bn)+12+strlen(message)+1+4);
-
- snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
-
- aim_icbm_makecookie(cookie);
-
- /* ICBM header */
- aim_im_puticbm(&bs, cookie, 0x0004, bn);
-
- /*
- * TLV t(0005)
- *
- * ICQ data (the UIN and the message).
- */
- byte_stream_put16(&bs, 0x0005);
- byte_stream_put16(&bs, 4 + 2+2+strlen(message)+1);
-
- /*
- * Your UIN
- */
- byte_stream_putuid(&bs, od);
-
- /*
- * TLV t(type) l(strlen(message)+1) v(message+NULL)
- */
- byte_stream_putle16(&bs, type);
- byte_stream_putle16(&bs, strlen(message)+1);
- byte_stream_putraw(&bs, (const guint8 *)message, strlen(message)+1);
-
- /*
- * TLV t(0006) l(0000) v()
- */
- byte_stream_put16(&bs, 0x0006);
- byte_stream_put16(&bs, 0x0000);
-
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
-
- byte_stream_destroy(&bs);
-
- return 0;
-}
-
-/*
- * XXX - I don't see when this would ever get called...
- */
-static int outgoingim(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
-{
- int ret = 0;
- aim_rxcallback_t userfunc;
- guchar cookie[8];
- guint16 channel;
- GSList *tlvlist;
- char *bn;
- int bnlen;
- guint16 icbmflags = 0;
- guint8 flag1 = 0, flag2 = 0;
- gchar *msg = NULL;
- aim_tlv_t *msgblock;
-
- /* ICBM Cookie. */
- aim_icbm_makecookie(cookie);
-
- /* Channel ID */
- channel = byte_stream_get16(bs);
-
- if (channel != 0x01) {
- purple_debug_misc("oscar", "icbm: ICBM recieved on unsupported channel. Ignoring. (chan = %04x)\n", channel);
- return 0;
- }
-
- bnlen = byte_stream_get8(bs);
- bn = byte_stream_getstr(bs, bnlen);
-
- tlvlist = aim_tlvlist_read(bs);
-
- if (aim_tlv_gettlv(tlvlist, 0x0003, 1))
- icbmflags |= AIM_IMFLAGS_ACK;
- if (aim_tlv_gettlv(tlvlist, 0x0004, 1))
- icbmflags |= AIM_IMFLAGS_AWAY;
-
- if ((msgblock = aim_tlv_gettlv(tlvlist, 0x0002, 1))) {
- ByteStream mbs;
- int featurelen, msglen;
-
- byte_stream_init(&mbs, msgblock->value, msgblock->length);
-
- byte_stream_get8(&mbs);
- byte_stream_get8(&mbs);
- for (featurelen = byte_stream_get16(&mbs); featurelen; featurelen--)
- byte_stream_get8(&mbs);
- byte_stream_get8(&mbs);
- byte_stream_get8(&mbs);
-
- msglen = byte_stream_get16(&mbs) - 4; /* final block length */
-
- flag1 = byte_stream_get16(&mbs);
- flag2 = byte_stream_get16(&mbs);
-
- msg = byte_stream_getstr(&mbs, msglen);
- }
-
- if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
- ret = userfunc(od, conn, frame, channel, bn, msg, icbmflags, flag1, flag2);
-
- g_free(bn);
- g_free(msg);
- aim_tlvlist_free(tlvlist);
-
- return ret;
-}
-
-/*
- * Ahh, the joys of nearly ridiculous over-engineering.
- *
- * Not only do AIM ICBM's support multiple channels. Not only do they
- * support multiple character sets. But they support multiple character
- * sets / encodings within the same ICBM.
- *
- * These multipart messages allow for complex space savings techniques, which
- * seem utterly unnecessary by today's standards. In fact, there is only
- * one client still in popular use that still uses this method: AOL for the
- * Macintosh, Version 5.0. Obscure, yes, I know.
- *
- * In modern (non-"legacy") clients, if the user tries to send a character
- * that is not ISO-8859-1 or ASCII, the client will send the entire message
- * as UNICODE, meaning that every character in the message will occupy the
- * full 16 bit UNICODE field, even if the high order byte would be zero.
- * Multipart messages prevent this wasted space by allowing the client to
- * only send the characters in UNICODE that need to be sent that way, and
- * the rest of the message can be sent in whatever the native character
- * set is (probably ASCII).
- *
- * An important note is that sections will be displayed in the order that
- * they appear in the ICBM. There is no facility for merging or rearranging
- * sections at run time. So if you have, say, ASCII then UNICODE then ASCII,
- * you must supply two ASCII sections with a UNICODE in the middle, and incur
- * the associated overhead.
- *
- * Normally I would have laughed and given a firm 'no' to supporting this
- * seldom-used feature, but something is attracting me to it. In the future,
- * it may be possible to abuse this to send mixed-media messages to other
- * open source clients (like encryption or something) -- see faimtest for
- * examples of how to do this.
- *
- * I would definitely recommend avoiding this feature unless you really
- * know what you are doing, and/or you have something neat to do with it.
- *
- */
-int aim_mpmsg_init(OscarData *od, aim_mpmsg_t *mpm)
-{
-
- memset(mpm, 0, sizeof(aim_mpmsg_t));
-
- return 0;
-}
-
-static int mpmsg_addsection(OscarData *od, aim_mpmsg_t *mpm, guint16 charset, guint16 charsubset, gchar *data, guint16 datalen)
-{
- aim_mpmsg_section_t *sec;
-
- sec = g_malloc(sizeof(aim_mpmsg_section_t));
-
- sec->charset = charset;
- sec->charsubset = charsubset;
- sec->data = data;
- sec->datalen = datalen;
- sec->next = NULL;
-
- if (!mpm->parts)
- mpm->parts = sec;
- else {
- aim_mpmsg_section_t *cur;
-
- for (cur = mpm->parts; cur->next; cur = cur->next)
- ;
- cur->next = sec;
- }
-
- mpm->numparts++;
-
- return 0;
-}
-
-int aim_mpmsg_addraw(OscarData *od, aim_mpmsg_t *mpm, guint16 charset, guint16 charsubset, const gchar *data, guint16 datalen)
-{
- gchar *dup;
-
- dup = g_malloc(datalen);
- memcpy(dup, data, datalen);
-
- if (mpmsg_addsection(od, mpm, charset, charsubset, dup, datalen) == -1) {
- g_free(dup);
- return -1;
- }
-
- return 0;
-}
-
-/* XXX - should provide a way of saying ISO-8859-1 specifically */
-int aim_mpmsg_addascii(OscarData *od, aim_mpmsg_t *mpm, const char *ascii)
-{
- gchar *dup;
-
- if (!(dup = g_strdup(ascii)))
- return -1;
-
- if (mpmsg_addsection(od, mpm, 0x0000, 0x0000, dup, strlen(ascii)) == -1) {
- g_free(dup);
- return -1;
- }
-
- return 0;
-}
-
-int aim_mpmsg_addunicode(OscarData *od, aim_mpmsg_t *mpm, const guint16 *unicode, guint16 unicodelen)
-{
- gchar *buf;
- ByteStream bs;
- int i;
-
- buf = g_malloc(unicodelen * 2);
-
- byte_stream_init(&bs, (guchar *)buf, unicodelen * 2);
-
- /* We assume unicode is in /host/ byte order -- convert to network */
- for (i = 0; i < unicodelen; i++)
- byte_stream_put16(&bs, unicode[i]);
-
- if (mpmsg_addsection(od, mpm, 0x0002, 0x0000, buf, byte_stream_curpos(&bs)) == -1) {
- g_free(buf);
- return -1;
- }
-
- return 0;
-}
-
-void aim_mpmsg_free(OscarData *od, aim_mpmsg_t *mpm)
-{
- aim_mpmsg_section_t *cur;
-
- for (cur = mpm->parts; cur; ) {
- aim_mpmsg_section_t *tmp;
-
- tmp = cur->next;
- g_free(cur->data);
- g_free(cur);
- cur = tmp;
- }
-
- mpm->numparts = 0;
- mpm->parts = NULL;
-
- return;
-}
-
-/*
- * Start by building the multipart structures, then pick the first
- * human-readable section and stuff it into args->msg so no one gets
- * suspicious.
- */
-static int incomingim_ch1_parsemsgs(OscarData *od, aim_userinfo_t *userinfo, guint8 *data, int len, struct aim_incomingim_ch1_args *args)
+static void
+incomingim_ch1_parsemsg(OscarData *od, aim_userinfo_t *userinfo, ByteStream *message, struct aim_incomingim_ch1_args *args)
{
- /* Should this be ASCII -> UNICODE -> Custom */
- static const guint16 charsetpri[] = {
- AIM_CHARSET_ASCII, /* ASCII first */
- AIM_CHARSET_LATIN_1, /* then ISO-8859-1 */
- AIM_CHARSET_UNICODE, /* UNICODE as last resort */
- };
- static const int charsetpricount = 3;
- int i;
- ByteStream mbs;
- aim_mpmsg_section_t *sec;
-
- byte_stream_init(&mbs, data, len);
-
- while (byte_stream_empty(&mbs)) {
- guint16 msglen, flag1, flag2;
- gchar *msgbuf;
-
- byte_stream_get8(&mbs); /* 01 */
- byte_stream_get8(&mbs); /* 01 */
-
- /* Message string length, including character set info. */
- msglen = byte_stream_get16(&mbs);
- if (msglen > byte_stream_empty(&mbs))
- {
- purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s. They are probably trying to do something malicious.\n", userinfo->bn);
- break;
- }
-
- /* Character set info */
- flag1 = byte_stream_get16(&mbs);
- flag2 = byte_stream_get16(&mbs);
-
- /* Message. */
- msglen -= 4;
-
- /*
- * For now, we don't care what the encoding is. Just copy
- * it into a multipart struct and deal with it later. However,
- * always pad the ending with a NULL. This makes it easier
- * to treat ASCII sections as strings. It won't matter for
- * UNICODE or binary data, as you should never read past
- * the specified data length, which will not include the pad.
- *
- * XXX - There's an API bug here. For sending, the UNICODE is
- * given in host byte order (aim_mpmsg_addunicode), but here
- * the received messages are given in network byte order.
- *
- */
- msgbuf = (gchar *)byte_stream_getraw(&mbs, msglen);
- mpmsg_addsection(od, &args->mpmsg, flag1, flag2, msgbuf, msglen);
-
- } /* while */
-
- args->icbmflags |= AIM_IMFLAGS_MULTIPART; /* always set */
-
+ PurpleAccount *account = purple_connection_get_account(od->gc);
/*
- * Clients that support multiparts should never use args->msg, as it
- * will point to an arbitrary section.
- *
- * Here, we attempt to provide clients that do not support multipart
- * messages with something to look at -- hopefully a human-readable
- * string. But, failing that, a UNICODE message, or nothing at all.
- *
- * Which means that even if args->msg is NULL, it does not mean the
- * message was blank.
- *
+ * We're interested in the inner TLV 0x101, which contains precious, precious message.
*/
- for (i = 0; i < charsetpricount; i++) {
- for (sec = args->mpmsg.parts; sec; sec = sec->next) {
-
- if (sec->charset != charsetpri[i])
- continue;
-
- /* Great. We found one. Fill it in. */
- args->charset = sec->charset;
- args->charsubset = sec->charsubset;
-
- /* Set up the simple flags */
- switch (args->charsubset)
- {
- case 0x0000:
- /* standard subencoding? */
- break;
- case 0x000b:
- args->icbmflags |= AIM_IMFLAGS_SUBENC_MACINTOSH;
- break;
- case 0xffff:
- /* no subencoding */
- break;
- default:
- break;
- }
-
- args->msg = sec->data;
- args->msglen = sec->datalen;
-
- return 0;
+ while (byte_stream_bytes_left(message) >= 4) {
+ guint16 type = byte_stream_get16(message);
+ guint16 length = byte_stream_get16(message);
+ if (type == 0x101) {
+ gchar *msg;
+ guint16 msglen = length - 4; /* charset + charsubset */
+ guint16 charset = byte_stream_get16(message);
+ byte_stream_advance(message, 2); /* charsubset */
+
+ msg = byte_stream_getstr(message, msglen);
+ args->msg = oscar_decode_im(account, userinfo->bn, charset, msg, msglen);
+ } else {
+ byte_stream_advance(message, length);
}
}
-
- /* No human-readable sections found. Oh well. */
- args->charset = args->charsubset = 0xffff;
- args->msg = NULL;
- args->msglen = 0;
-
- return 0;
}
-static int incomingim_ch1(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, ByteStream *bs, guint8 *cookie)
+static int
+incomingim_ch1(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, ByteStream *bs, guint8 *cookie)
{
- guint16 type, length, magic1, msglen = 0;
+ guint16 type, length;
aim_rxcallback_t userfunc;
int ret = 0;
- int rev = 0;
struct aim_incomingim_ch1_args args;
unsigned int endpos;
memset(&args, 0, sizeof(args));
- aim_mpmsg_init(od, &args.mpmsg);
-
/*
* This used to be done using tlvchains. For performance reasons,
* I've changed it to process the TLVs in-place. This avoids lots
* of per-IM memory allocations.
*/
- while (byte_stream_empty(bs) >= 4)
+ while (byte_stream_bytes_left(bs) >= 4)
{
type = byte_stream_get16(bs);
length = byte_stream_get16(bs);
- if (length > byte_stream_empty(bs))
+ if (length > byte_stream_bytes_left(bs))
{
purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s. They are probably trying to do something malicious.\n", userinfo->bn);
break;
@@ -1652,93 +963,20 @@ static int incomingim_ch1(OscarData *od, FlapConnection *conn, aim_module_t *mod
endpos = byte_stream_curpos(bs) + length;
if (type == 0x0002) { /* Message Block */
-
- /*
- * This TLV consists of the following:
- * - 0501 -- Unknown
- * - Features: Don't know how to interpret these
- * - 0101 -- Unknown
- * - Message
- *
- * Slick and possible others reverse 'Features' and 'Messages' section.
- * Thus, the TLV could have following layout:
- * - 0101 -- Unknown (possibly magic for message section)
- * - Message
- * - 0501 -- Unknown (possibly magic for features section)
- * - Features: Don't know how to interpret these
- */
-
- magic1 = byte_stream_get16(bs); /* 0501 or 0101 */
- if (magic1 == 0x101) /* Bad, message comes before attributes */
- {
- /* Jump to the features section */
- msglen = byte_stream_get16(bs);
- bs->offset += msglen;
- rev = 1;
-
- magic1 = byte_stream_get16(bs); /* 0501 */
- }
-
- if (magic1 != 0x501)
- {
- purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s. They are probably trying to do something malicious.\n", userinfo->bn);
- break;
- }
-
- args.featureslen = byte_stream_get16(bs);
- if (args.featureslen > byte_stream_empty(bs))
- {
- purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s. They are probably trying to do something malicious.\n", userinfo->bn);
- break;
- }
- if (args.featureslen == 0)
- {
- args.features = NULL;
- }
- else
- {
- args.features = byte_stream_getraw(bs, args.featureslen);
- args.icbmflags |= AIM_IMFLAGS_CUSTOMFEATURES;
- }
-
- if (rev)
- {
- /* Fix buffer back to message */
- bs->offset -= args.featureslen + 2 + 2 + msglen + 2 + 2;
- }
-
- magic1 = byte_stream_get16(bs); /* 01 01 */
- if (magic1 != 0x101) /* Bad, message comes before attributes */
- {
- purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s. They are probably trying to do something malicious.\n", userinfo->bn);
- break;
- }
- msglen = byte_stream_get16(bs);
-
- /*
- * The rest of the TLV contains one or more message
- * blocks...
- */
- incomingim_ch1_parsemsgs(od, userinfo, bs->data + bs->offset - 2 - 2 /* XXX evil!!! */, msglen + 2 + 2, &args);
-
+ ByteStream tlv02;
+ byte_stream_init(&tlv02, bs->data + bs->offset, length);
+ incomingim_ch1_parsemsg(od, userinfo, &tlv02, &args);
} else if (type == 0x0003) { /* Server Ack Requested */
-
args.icbmflags |= AIM_IMFLAGS_ACK;
-
} else if (type == 0x0004) { /* Message is Auto Response */
-
args.icbmflags |= AIM_IMFLAGS_AWAY;
-
} else if (type == 0x0006) { /* Message was received offline. */
-
/*
* This flag is set on incoming offline messages for both
* AIM and ICQ accounts.
*/
args.icbmflags |= AIM_IMFLAGS_OFFLINE;
-
} else if (type == 0x0008) { /* I-HAVE-A-REALLY-PURTY-ICON Flag */
-
args.iconlen = byte_stream_get32(bs);
byte_stream_get16(bs); /* 0x0001 */
args.iconsum = byte_stream_get16(bs);
@@ -1756,39 +994,16 @@ static int incomingim_ch1(OscarData *od, FlapConnection *conn, aim_module_t *mod
*/
if (args.iconlen)
args.icbmflags |= AIM_IMFLAGS_HASICON;
-
} else if (type == 0x0009) {
-
args.icbmflags |= AIM_IMFLAGS_BUDDYREQ;
-
} else if (type == 0x000b) { /* Non-direct connect typing notification */
-
args.icbmflags |= AIM_IMFLAGS_TYPINGNOT;
-
} else if (type == 0x0016) {
-
/*
* UTC timestamp for when the message was sent. Only
* provided for offline messages.
*/
args.timestamp = byte_stream_get32(bs);
-
- } else if (type == 0x0017) {
-
- if (length > byte_stream_empty(bs))
- {
- purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s. They are probably trying to do something malicious.\n", userinfo->bn);
- break;
- }
- g_free(args.extdata);
- args.extdatalen = length;
- if (args.extdatalen == 0)
- args.extdata = NULL;
- else
- args.extdata = byte_stream_getraw(bs, args.extdatalen);
-
- } else {
- purple_debug_misc("oscar", "incomingim_ch1: unknown TLV 0x%04x (len %d)\n", type, length);
}
/*
@@ -1806,10 +1021,7 @@ static int incomingim_ch1(OscarData *od, FlapConnection *conn, aim_module_t *mod
if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
ret = userfunc(od, conn, frame, channel, userinfo, &args);
- aim_mpmsg_free(od, &args.mpmsg);
- g_free(args.features);
- g_free(args.extdata);
-
+ g_free(args.msg);
return ret;
}
@@ -1835,7 +1047,7 @@ incomingim_ch2_buddylist(OscarData *od, FlapConnection *conn, aim_module_t *mod,
* ...
* ...
*/
- while (byte_stream_empty(servdata))
+ while (byte_stream_bytes_left(servdata))
{
guint16 gnlen, numb;
int i;
@@ -1907,7 +1119,7 @@ incomingim_ch2_chat(OscarData *od, FlapConnection *conn, aim_module_t *mod, Flap
static void
incomingim_ch2_icqserverrelay_free(OscarData *od, IcbmArgsCh2 *args)
{
- g_free((char *)args->info.rtfmsg.rtfmsg);
+ g_free((char *)args->info.rtfmsg.msg);
}
/*
@@ -1921,33 +1133,34 @@ incomingim_ch2_icqserverrelay_free(OscarData *od, IcbmArgsCh2 *args)
static void
incomingim_ch2_icqserverrelay(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, aim_userinfo_t *userinfo, IcbmArgsCh2 *args, ByteStream *servdata)
{
- guint16 hdrlen, anslen, msglen;
-
- if (byte_stream_empty(servdata) < 24)
- /* Someone sent us a short server relay ICBM. Weird. (Maybe?) */
- return;
+ guint16 hdrlen, msglen;
- hdrlen = byte_stream_getle16(servdata);
- byte_stream_advance(servdata, hdrlen);
+ args->destructor = (void *)incomingim_ch2_icqserverrelay_free;
- hdrlen = byte_stream_getle16(servdata);
+#define SKIP_HEADER(expected_hdrlen) \
+ hdrlen = byte_stream_getle16(servdata); \
+ if (hdrlen != expected_hdrlen) { \
+ purple_debug_warning("oscar", "Expected to find a header with length " #expected_hdrlen "; ignoring message"); \
+ return; \
+ } \
byte_stream_advance(servdata, hdrlen);
- args->info.rtfmsg.msgtype = byte_stream_getle16(servdata);
+ SKIP_HEADER(0x001b);
+ SKIP_HEADER(0x000e);
- anslen = byte_stream_getle32(servdata);
- byte_stream_advance(servdata, anslen);
+ args->info.rtfmsg.msgtype = byte_stream_get8(servdata);
+ /*
+ * Copied from http://iserverd.khstu.ru/oscar/message.html:
+ * xx byte message flags
+ * xx xx word (LE) status code
+ * xx xx word (LE) priority code
+ *
+ * We don't need any of these, so just skip them.
+ */
+ byte_stream_advance(servdata, 1 + 2 + 2);
msglen = byte_stream_getle16(servdata);
- args->info.rtfmsg.rtfmsg = byte_stream_getstr(servdata, msglen);
-
- args->info.rtfmsg.fgcolor = byte_stream_getle32(servdata);
- args->info.rtfmsg.bgcolor = byte_stream_getle32(servdata);
-
- hdrlen = byte_stream_getle32(servdata);
- byte_stream_advance(servdata, hdrlen);
-
- args->destructor = (void *)incomingim_ch2_icqserverrelay_free;
+ args->info.rtfmsg.msg = byte_stream_getstr(servdata, msglen);
}
static void
@@ -2107,7 +1320,7 @@ static int incomingim_ch2(OscarData *od, FlapConnection *conn, aim_module_t *mod
/*
* Terminate connection/error code. 0x0001 means the other user
- * canceled the connection.
+ * cancelled the connection.
*/
if (aim_tlv_gettlv(list2, 0x000b, 1))
args.errorcode = aim_tlv_get16(list2, 0x000b, 1);
@@ -2132,20 +1345,6 @@ static int incomingim_ch2(OscarData *od, FlapConnection *conn, aim_module_t *mod
if (aim_tlv_gettlv(list2, 0x000e, 1))
args.language = aim_tlv_getstr(list2, 0x000e, 1);
-#if 0
- /*
- * Unknown -- no value
- *
- * Maybe means we should connect directly to transfer the file?
- * Also used in ICQ Lite Beta 4.0 URLs. Also empty.
- */
- /* I don't think this indicates a direct transfer; this flag is
- * also present in a stage 1 proxied file send request -- Jonathan */
- if (aim_tlv_gettlv(list2, 0x000f, 1)) {
- /* Unhandled */
- }
-#endif
-
/*
* Flag meaning we should proxy the file transfer through an AIM server
*/
@@ -2340,38 +1539,6 @@ static int incomingim(OscarData *od, FlapConnection *conn, aim_module_t *mod, Fl
return ret;
}
-/*
- * Subtype 0x0008 - Send a warning to bn.
- *
- * Flags:
- * AIM_WARN_ANON Send as an anonymous (doesn't count as much)
- *
- * returns -1 on error (couldn't alloc packet), 0 on success.
- *
- */
-int aim_im_warn(OscarData *od, FlapConnection *conn, const char *bn, guint32 flags)
-{
- ByteStream bs;
- aim_snacid_t snacid;
-
- if (!od || !conn || !bn)
- return -EINVAL;
-
- byte_stream_new(&bs, strlen(bn)+3);
-
- snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0008, 0x0000, bn, strlen(bn)+1);
-
- byte_stream_put16(&bs, (flags & AIM_WARN_ANON) ? 0x0001 : 0x0000);
- byte_stream_put8(&bs, strlen(bn));
- byte_stream_putstr(&bs, bn);
-
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0008, 0x0000, snacid, &bs);
-
- byte_stream_destroy(&bs);
-
- return 0;
-}
-
/* Subtype 0x000a */
static int missedcall(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
{
@@ -2380,7 +1547,7 @@ static int missedcall(OscarData *od, FlapConnection *conn, aim_module_t *mod, Fl
guint16 channel, nummissed, reason;
aim_userinfo_t userinfo;
- while (byte_stream_empty(bs)) {
+ while (byte_stream_bytes_left(bs)) {
channel = byte_stream_get16(bs);
aim_info_extract(od, bs, &userinfo);
@@ -2400,9 +1567,7 @@ static int missedcall(OscarData *od, FlapConnection *conn, aim_module_t *mod, Fl
* Subtype 0x000b
*
* Possible codes:
- * AIM_TRANSFER_DENY_NOTSUPPORTED -- "client does not support"
* AIM_TRANSFER_DENY_DECLINE -- "client has declined transfer"
- * AIM_TRANSFER_DENY_NOTACCEPTING -- "client is not accepting transfers"
*
*/
int aim_im_denytransfer(OscarData *od, const char *bn, const guchar *cookie, guint16 code)
@@ -2429,186 +1594,57 @@ int aim_im_denytransfer(OscarData *od, const char *bn, const guchar *cookie, gui
aim_tlvlist_write(&bs, &tlvlist);
aim_tlvlist_free(tlvlist);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x000b, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x000b, snacid, &bs);
byte_stream_destroy(&bs);
return 0;
}
-static void parse_status_note_text(OscarData *od, guchar *cookie, char *bn, ByteStream *bs)
+/*
+ * Subtype 0x000b.
+ * Send confirmation for a channel 2 message (Miranda wants it by default).
+ */
+void
+aim_im_send_icq_confirmation(OscarData *od, const char *bn, const guchar *cookie)
{
- struct aim_icq_info *info;
- struct aim_icq_info *prev_info;
- char *response;
- char *encoding;
- char *stripped_encoding;
- char *status_note_title;
- char *status_note_text;
- char *stripped_status_note_text;
- char *status_note;
- guint32 length;
- guint16 version;
- guint32 capability;
- guint8 message_type;
- guint16 status_code;
- guint16 text_length;
- guint32 request_length;
- guint32 response_length;
- guint32 encoding_length;
- PurpleAccount *account;
- PurpleBuddy *buddy;
- PurplePresence *presence;
- PurpleStatus *status;
-
- for (prev_info = NULL, info = od->icq_info; info != NULL; prev_info = info, info = info->next)
- {
- if (memcmp(&info->icbm_cookie, cookie, 8) == 0)
- {
- if (prev_info == NULL)
- od->icq_info = info->next;
- else
- prev_info->next = info->next;
-
- break;
- }
- }
-
- if (info == NULL)
- return;
-
- status_note_title = info->status_note_title;
- g_free(info);
-
- length = byte_stream_getle16(bs);
- if (length != 27) {
- purple_debug_misc("oscar", "clientautoresp: incorrect header "
- "size; expected 27, received %u.\n", length);
- g_free(status_note_title);
- return;
- }
-
- version = byte_stream_getle16(bs);
- if (version != 9) {
- purple_debug_misc("oscar", "clientautoresp: incorrect version; "
- "expected 9, received %u.\n", version);
- g_free(status_note_title);
- return;
- }
-
- capability = aim_locate_getcaps(od, bs, 0x10);
- if (capability != OSCAR_CAPABILITY_EMPTY) {
- purple_debug_misc("oscar", "clientautoresp: plugin ID is not null.\n");
- g_free(status_note_title);
- return;
- }
-
- byte_stream_advance(bs, 2); /* unknown */
- byte_stream_advance(bs, 4); /* client capabilities flags */
- byte_stream_advance(bs, 1); /* unknown */
- byte_stream_advance(bs, 2); /* downcouner? */
-
- length = byte_stream_getle16(bs);
- if (length != 14) {
- purple_debug_misc("oscar", "clientautoresp: incorrect header "
- "size; expected 14, received %u.\n", length);
- g_free(status_note_title);
- return;
- }
-
- byte_stream_advance(bs, 2); /* downcounter? */
- byte_stream_advance(bs, 12); /* unknown */
-
- message_type = byte_stream_get8(bs);
- if (message_type != 0x1a) {
- purple_debug_misc("oscar", "clientautoresp: incorrect message "
- "type; expected 0x1a, received 0x%x.\n", message_type);
- g_free(status_note_title);
- return;
- }
-
- byte_stream_advance(bs, 1); /* message flags */
-
- status_code = byte_stream_getle16(bs);
- if (status_code != 0) {
- purple_debug_misc("oscar", "clientautoresp: incorrect status "
- "code; expected 0, received %u.\n", status_code);
- g_free(status_note_title);
- return;
- }
-
- byte_stream_advance(bs, 2); /* priority code */
-
- text_length = byte_stream_getle16(bs);
- byte_stream_advance(bs, text_length); /* text */
-
- length = byte_stream_getle16(bs);
- byte_stream_advance(bs, 18); /* unknown */
-
- request_length = byte_stream_getle32(bs);
- if (length != 18 + 4 + request_length + 17) {
- purple_debug_misc("oscar", "clientautoresp: incorrect block; "
- "expected length is %u, got %u.\n",
- 18 + 4 + request_length + 17, length);
- g_free(status_note_title);
- return;
- }
-
- byte_stream_advance(bs, request_length); /* x request */
- byte_stream_advance(bs, 17); /* unknown */
-
- length = byte_stream_getle32(bs);
- response_length = byte_stream_getle32(bs);
- response = byte_stream_getstr(bs, response_length);
- encoding_length = byte_stream_getle32(bs);
- if (length != 4 + response_length + 4 + encoding_length) {
- purple_debug_misc("oscar", "clientautoresp: incorrect block; "
- "expected length is %u, got %u.\n",
- 4 + response_length + 4 + encoding_length, length);
- g_free(status_note_title);
- g_free(response);
- return;
- }
-
- encoding = byte_stream_getstr(bs, encoding_length);
-
- account = purple_connection_get_account(od->gc);
-
- stripped_encoding = oscar_encoding_extract(encoding);
- status_note_text = oscar_encoding_to_utf8(account, stripped_encoding, response, response_length);
- stripped_status_note_text = purple_markup_strip_html(status_note_text);
+ ByteStream bs;
+ aim_snacid_t snacid;
+ guint32 header_size, data_size;
+ guint16 cookie2 = (guint16)g_random_int();
- if (stripped_status_note_text != NULL && stripped_status_note_text[0] != 0)
- status_note = g_strdup_printf("%s: %s", status_note_title, stripped_status_note_text);
- else
- status_note = g_strdup(status_note_title);
+ purple_debug_misc("oscar", "Sending message ack to %s\n", bn);
- g_free(status_note_title);
- g_free(response);
- g_free(encoding);
- g_free(stripped_encoding);
- g_free(status_note_text);
- g_free(stripped_status_note_text);
+ header_size = 8 + 2 + 1 + strlen(bn) + 2;
+ data_size = 2 + 1 + 16 + 4*2 + 2*3 + 4*3 + 1*2 + 2*3 + 1;
+ byte_stream_new(&bs, header_size + data_size);
- buddy = purple_find_buddy(account, bn);
- if (buddy == NULL)
- {
- purple_debug_misc("oscar", "clientautoresp: buddy %s was not found.\n", bn);
- g_free(status_note);
- return;
- }
-
- purple_debug_misc("oscar", "clientautoresp: setting status "
- "message to \"%s\".\n", status_note);
-
- presence = purple_buddy_get_presence(buddy);
- status = purple_presence_get_active_status(presence);
+ /* The message header. */
+ aim_im_puticbm(&bs, cookie, 0x0002, bn);
+ byte_stream_put16(&bs, 0x0003); /* reason */
- purple_prpl_got_user_status(account, bn,
- purple_status_get_id(status),
- "message", status_note, NULL);
+ /* The actual message. */
+ byte_stream_putle16(&bs, 0x1b); /* subheader #1 length */
+ byte_stream_put8(&bs, 0x08); /* protocol version */
+ byte_stream_putcaps(&bs, OSCAR_CAPABILITY_EMPTY);
+ byte_stream_put32(&bs, 0x3); /* client features */
+ byte_stream_put32(&bs, 0x0004); /* DC type */
+ byte_stream_put16(&bs, cookie2); /* a cookie, chosen by fair dice roll */
+ byte_stream_putle16(&bs, 0x0e); /* header #2 len? */
+ byte_stream_put16(&bs, cookie2); /* the same cookie again */
+ byte_stream_put32(&bs, 0); /* unknown */
+ byte_stream_put32(&bs, 0); /* unknown */
+ byte_stream_put32(&bs, 0); /* unknown */
+ byte_stream_put8(&bs, 0x01); /* plain text message */
+ byte_stream_put8(&bs, 0x00); /* no message flags */
+ byte_stream_put16(&bs, 0x0000); /* no icq status */
+ byte_stream_put16(&bs, 0x0100); /* priority */
+ byte_stream_putle16(&bs, 1); /* query message len */
+ byte_stream_put8(&bs, 0x00); /* empty query message */
- g_free(status_note);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x000b, 0x0000, NULL, 0);
+ flap_connection_send_snac(od, flap_connection_findbygroup(od, SNAC_FAMILY_ICBM), SNAC_FAMILY_ICBM, 0x000b, snacid, &bs);
+ byte_stream_destroy(&bs);
}
/*
@@ -2625,10 +1661,9 @@ static int clientautoresp(OscarData *od, FlapConnection *conn, aim_module_t *mod
guchar *cookie;
guint8 bnlen;
char *xml = NULL;
- int hdrlen;
+ guint16 hdrlen;
int curpos;
- int num1,num2;
- char *desc, *title, *temp;
+ guint16 num1, num2;
PurpleAccount *account;
PurpleBuddy *buddy;
PurplePresence *presence;
@@ -2642,54 +1677,64 @@ static int clientautoresp(OscarData *od, FlapConnection *conn, aim_module_t *mod
if (channel == 0x0002)
{
- hdrlen = byte_stream_getle16(bs);
- if ( ((hdrlen == 27 ) && (bs->len > (27 + 51)))) {
- byte_stream_advance(bs, 51);
- num1 = byte_stream_getle16(bs);
- num2 = byte_stream_getle16(bs);
- purple_debug_misc("oscar", "X-Status: Num1 %i, num2 %i\n",num1, num2);
-
- if(((num1 == 0x4f00)&&(num2 == 0x3b00))) {
- byte_stream_advance(bs, 86);
- curpos = byte_stream_curpos(bs);
- xml = byte_stream_getstr(bs, bs->len - curpos);
- purple_debug_misc("oscar", "X-Status: Received XML reply\n");
- if(xml) {
- /* purple_debug_misc("oscar", "X-Status: XML reply: %s\n", (const char*) xml); */
- if ((desc=strstr(xml,"&lt;desc&gt;")) != NULL) {
- temp=strstr(xml,"&lt;/desc&gt;");
- temp[0]=0;
- desc=desc+12;
- }
- if ((title=strstr(xml,"&lt;title&gt;")) != NULL) {
- temp=strstr(xml,"&lt;/title&gt;");
- temp[0]=0;
- title=title+13;
- } else {
- title="";
- }
- strcpy(xml,title);
- if (desc) {
- strcat(xml, " - ");
- strcat(xml, desc);
+ hdrlen = byte_stream_getle16(bs);
+ if (hdrlen == 27 && bs->len > (27 + 51)) {
+ byte_stream_advance(bs, 51);
+ num1 = byte_stream_getle16(bs);
+ num2 = byte_stream_getle16(bs);
+ purple_debug_misc("oscar", "X-Status: num1 %hu, num2 %hu\n", num1, num2);
+
+ if (num1 == 0x4f00 && num2 == 0x3b00) {
+ byte_stream_advance(bs, 86);
+ curpos = byte_stream_curpos(bs);
+ xml = byte_stream_getstr(bs, bs->len - curpos);
+ purple_debug_misc("oscar", "X-Status: Received XML reply\n");
+ if (xml) {
+ GString *xstatus;
+ char *tmp1, *tmp2;
+
+ /* purple_debug_misc("oscar", "X-Status: XML reply: %s\n", xml); */
+
+ xstatus = g_string_new(NULL);
+
+ tmp1 = strstr(xml, "&lt;title&gt;");
+ if (tmp1 != NULL) {
+ tmp1 += 13;
+ tmp2 = strstr(tmp1, "&lt;/title&gt;");
+ if (tmp2 != NULL)
+ g_string_append_len(xstatus, tmp1, tmp2 - tmp1);
+ }
+ tmp1 = strstr(xml, "&lt;desc&gt;");
+ if (tmp1 != NULL) {
+ tmp1 += 12;
+ tmp2 = strstr(tmp1, "&lt;/desc&gt;");
+ if (tmp2 != NULL) {
+ if (xstatus->len > 0)
+ g_string_append(xstatus, " - ");
+ g_string_append_len(xstatus, tmp1, tmp2 - tmp1);
+ }
}
- purple_debug_misc("oscar", "X-Status reply: %s\n", (const char*)xml);
- account = purple_connection_get_account(od->gc);
- buddy = purple_find_buddy(account, bn);
- presence = purple_buddy_get_presence(buddy);
- status = purple_presence_get_active_status(presence);
- purple_prpl_got_user_status(account, bn,
- purple_status_get_id(status), "message", xml, NULL);
- } else {
- purple_debug_misc("oscar", "X-Status: Can't get XML reply string\n");
+ if (xstatus->len > 0) {
+ purple_debug_misc("oscar", "X-Status reply: %s\n", xstatus->str);
+ account = purple_connection_get_account(od->gc);
+ buddy = purple_find_buddy(account, bn);
+ presence = purple_buddy_get_presence(buddy);
+ status = purple_presence_get_active_status(presence);
+ purple_prpl_got_user_status(account, bn,
+ purple_status_get_id(status),
+ "message", xstatus->str, NULL);
+ }
+ g_string_free(xstatus, TRUE);
+ } else {
+ purple_debug_misc("oscar", "X-Status: Can't get XML reply string\n");
}
- } else {
- purple_debug_misc("oscar", "X-Status: 0x0004, 0x000b not an xstatus reply\n" );
- /* if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
- ret = userfunc(od, conn, frame, channel, sn, reason); */
+ } else {
+ purple_debug_misc("oscar", "X-Status: 0x0004, 0x000b not an xstatus reply\n");
+ /* if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
+ ret = userfunc(od, conn, frame, channel, sn, reason); */
}
-
- }
+
+ }
} else if (channel == 0x0004) { /* ICQ message */
switch (reason) {
@@ -2754,16 +1799,10 @@ static int clientautoresp(OscarData *od, FlapConnection *conn, aim_module_t *mod
}
/*
- * Subtype 0x000c - Receive an ack after sending an ICBM.
- *
- * You have to have send the message with the AIM_IMFLAGS_ACK flag set
- * (TLV t(0003)). The ack contains the ICBM header of the message you
- * sent.
- *
+ * Subtype 0x000c - Receive an ack after sending an ICBM. The ack contains the ICBM header of the message you sent.
*/
static int msgack(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
{
- aim_rxcallback_t userfunc;
guint16 ch;
guchar *cookie;
char *bn;
@@ -2773,8 +1812,7 @@ static int msgack(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFr
ch = byte_stream_get16(bs);
bn = byte_stream_getstr(bs, byte_stream_get8(bs));
- if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
- ret = userfunc(od, conn, frame, ch, bn);
+ purple_debug_info("oscar", "Sent message to %s.\n", bn);
g_free(bn);
g_free(cookie);
@@ -2825,7 +1863,7 @@ int aim_im_sendmtn(OscarData *od, guint16 channel, const char *bn, guint16 event
if (!bn)
return -EINVAL;
- byte_stream_new(&bs, 11+strlen(bn)+2);
+ byte_stream_new(&bs, 11 + strlen(bn) + 2);
snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0014, 0x0000, NULL, 0);
@@ -2849,7 +1887,7 @@ int aim_im_sendmtn(OscarData *od, guint16 channel, const char *bn, guint16 event
*/
byte_stream_put16(&bs, event);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0014, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0014, snacid, &bs);
byte_stream_destroy(&bs);
@@ -2871,38 +1909,36 @@ int icq_im_xstatus_request(OscarData *od, const char *sn)
char *statxml;
int xmllen;
- static const guint8 pluginid[] =
- {
- 0x09, 0x46, 0x13, 0x49, 0x4C, 0x7F, 0x11, 0xD1,
- 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00
+ static const guint8 pluginid[] = {
+ 0x09, 0x46, 0x13, 0x49, 0x4C, 0x7F, 0x11, 0xD1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00
};
-
- static const guint8 c_plugindata[] =
- {
- 0x1B, 0x00, 0x0A,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xD1, 0x0E, 0x00, 0xF9, 0xD1, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x01, 0x00,
- 0x01, 0x00, 0x00, 0x4F, 0x00, 0x3B, 0x60, 0xB3, 0xEF, 0xD8, 0x2A, 0x6C, 0x45, 0xA4, 0xE0, 0x9C,
- 0x5A, 0x5E, 0x67, 0xE8, 0x65, 0x08, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x53, 0x63, 0x72, 0x69, 0x70,
- 0x74, 0x20, 0x50, 0x6C, 0x75, 0x67, 0x2D, 0x69, 0x6E, 0x3A, 0x20, 0x52, 0x65, 0x6D, 0x6F, 0x74,
- 0x65, 0x20, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41,
- 0x72, 0x72, 0x69, 0x76, 0x65, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00
+
+ static const guint8 c_plugindata[] = {
+ 0x1B, 0x00, 0x0A,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xD1, 0x0E, 0x00, 0xF9, 0xD1, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x00, 0x4F, 0x00, 0x3B, 0x60, 0xB3, 0xEF, 0xD8, 0x2A, 0x6C, 0x45, 0xA4, 0xE0, 0x9C,
+ 0x5A, 0x5E, 0x67, 0xE8, 0x65, 0x08, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x53, 0x63, 0x72, 0x69, 0x70,
+ 0x74, 0x20, 0x50, 0x6C, 0x75, 0x67, 0x2D, 0x69, 0x6E, 0x3A, 0x20, 0x52, 0x65, 0x6D, 0x6F, 0x74,
+ 0x65, 0x20, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41,
+ 0x72, 0x72, 0x69, 0x76, 0x65, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00
};
-
+
if (!od || !(conn = flap_connection_findbygroup(od, 0x0004)))
return -EINVAL;
if (!sn)
return -EINVAL;
- fmt = "<N><QUERY>&lt;Q&gt;&lt;PluginID&gt;srvMng&lt;/PluginID&gt;&lt;/Q&gt;</QUERY><NOTIFY>&lt;srv&gt;&lt;id&gt;cAwaySrv&lt;/id&gt;&lt;req&gt;&lt;id&gt;AwayStat&lt;/id&gt;&lt;trans&gt;2&lt;/trans&gt;&lt;senderId&gt;%s&lt;/senderId&gt;&lt;/req&gt;&lt;/srv&gt;</NOTIFY></N>\r\n";
+ fmt = "<N><QUERY>&lt;Q&gt;&lt;PluginID&gt;srvMng&lt;/PluginID&gt;&lt;/Q&gt;</QUERY><NOTIFY>&lt;srv&gt;&lt;id&gt;cAwaySrv&lt;/id&gt;&lt;req&gt;&lt;id&gt;AwayStat&lt;/id&gt;&lt;trans&gt;2&lt;/trans&gt;&lt;senderId&gt;%s&lt;/senderId&gt;&lt;/req&gt;&lt;/srv&gt;</NOTIFY></N>\r\n";
account = purple_connection_get_account(od->gc);
xmllen = strlen(fmt) - 2 + strlen(account->username);
- statxml = (char*) g_malloc(xmllen);
+ statxml = g_malloc(xmllen);
snprintf(statxml, xmllen, fmt, account->username);
aim_icbm_makecookie(cookie);
@@ -2911,38 +1947,36 @@ int icq_im_xstatus_request(OscarData *od, const char *sn)
+ 2 + 2 + 8 + 16 + 2 + 2 + 2 + 2 + 2
+ 2 + 2 + sizeof(c_plugindata) + xmllen
+ 2 + 2);
-
+
snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
aim_im_puticbm(&bs, cookie, 0x0002, sn);
byte_stream_new(&header, (7*2) + 16 + 8 + 2 + sizeof(c_plugindata) + xmllen); /* TLV 0x0005 Stream + Size */
- byte_stream_new(&plugindata, (sizeof(c_plugindata) + xmllen));
-
byte_stream_put16(&header, 0x0000); /* Message Type: Request */
byte_stream_putraw(&header, cookie, sizeof(cookie)); /* Message ID */
byte_stream_putraw(&header, pluginid, sizeof(pluginid)); /* Plugin ID */
-
+
aim_tlvlist_add_16(&inner_tlvlist, 0x000a, 0x0001);
aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
-
+
/* Add Plugin Specific Data */
+ byte_stream_new(&plugindata, (sizeof(c_plugindata) + xmllen));
byte_stream_putraw(&plugindata, c_plugindata, sizeof(c_plugindata)); /* Content of TLV 0x2711 */
byte_stream_putstr(&plugindata, statxml);
aim_tlvlist_add_raw(&inner_tlvlist, 0x2711, (sizeof(c_plugindata) + xmllen), plugindata.data);
-
+
aim_tlvlist_write(&header, &inner_tlvlist);
-
-
+ aim_tlvlist_free(inner_tlvlist);
+
aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&header), header.data);
- aim_tlvlist_add_noval(&outer_tlvlist, 0x0003); /* Empty TLV 0x0003 */
-
+ aim_tlvlist_add_noval(&outer_tlvlist, 0x0003); /* Empty TLV 0x0003 */
+
aim_tlvlist_write(&bs, &outer_tlvlist);
-
+
purple_debug_misc("oscar", "X-Status Request\n");
- flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs, TRUE);
+ flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x0006, snacid, &bs, TRUE);
- aim_tlvlist_free(inner_tlvlist);
aim_tlvlist_free(outer_tlvlist);
byte_stream_destroy(&header);
byte_stream_destroy(&plugindata);
@@ -2959,58 +1993,65 @@ int icq_relay_xstatus(OscarData *od, const char *sn, const guchar *cookie)
aim_snacid_t snacid;
PurpleAccount *account;
PurpleStatus *status;
- const char *fmt;
- const char *formatted_msg;
- char *msg;
- char *statxml;
+ const char *fmt;
+ const char *formatted_msg;
+ char *msg;
+ char *statxml;
const char *title;
int len;
-
+
static const guint8 plugindata[] = {
- 0x1B, 0x00,
- 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xD1, 0x0E, 0x00, 0xF9, 0xD1,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x4F,
- 0x00, 0x3B, 0x60, 0xB3, 0xEF, 0xD8, 0x2A, 0x6C, 0x45, 0xA4, 0xE0,
- 0x9C, 0x5A, 0x5E, 0x67, 0xE8, 0x65, 0x08, 0x00, 0x2A, 0x00, 0x00,
- 0x00, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x50, 0x6C, 0x75,
- 0x67, 0x2D, 0x69, 0x6E, 0x3A, 0x20, 0x52, 0x65, 0x6D, 0x6F, 0x74,
- 0x65, 0x20, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
- 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x72, 0x72, 0x69, 0x76, 0x65, 0x00,
- 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0xF3, 0x01, 0x00, 0x00, 0xEF, 0x01, 0x00, 0x00
- };
+ 0x1B, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xD1, 0x0E, 0x00, 0xF9, 0xD1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x4F,
+ 0x00, 0x3B, 0x60, 0xB3, 0xEF, 0xD8, 0x2A, 0x6C, 0x45, 0xA4, 0xE0,
+ 0x9C, 0x5A, 0x5E, 0x67, 0xE8, 0x65, 0x08, 0x00, 0x2A, 0x00, 0x00,
+ 0x00, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x50, 0x6C, 0x75,
+ 0x67, 0x2D, 0x69, 0x6E, 0x3A, 0x20, 0x52, 0x65, 0x6D, 0x6F, 0x74,
+ 0x65, 0x20, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+ 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x72, 0x72, 0x69, 0x76, 0x65, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xF3, 0x01, 0x00, 0x00, 0xEF, 0x01, 0x00, 0x00
+ };
fmt = "<NR><RES>&lt;ret event='OnRemoteNotification'&gt;&lt;srv&gt;&lt;id&gt;cAwaySrv&lt;/id&gt;&lt;val srv_id='cAwaySrv'&gt;&lt;Root&gt;&lt;CASXtraSetAwayMessage&gt;&lt;/CASXtraSetAwayMessage&gt;&l t;uin&gt;%s&lt;/uin&gt;&lt;index&gt;1&lt;/index&gt;&lt;title&gt;%s&lt;/title&gt;&lt;desc&gt;%s&lt;/desc&gt;&lt;/Root&gt;&lt;/val&gt;&lt;/srv&gt;&lt;srv&gt;&lt;id&gt;cRandomizerSrv&lt;/id&gt;&lt;val srv_id='cRandomizerSrv'&gt;undefined&lt;/val&gt;&lt;/srv&gt;&lt;/ret&gt;</RES></NR>\r\n";
-
-
+
if (!od || !(conn = flap_connection_findbygroup(od, 0x0002)))
return -EINVAL;
if (!sn)
return -EINVAL;
-
+
account = purple_connection_get_account(od->gc);
- if(!account) return -EINVAL;
-
-/* if (!strcmp(account->username, sn))
+ if (!account)
+ return -EINVAL;
+
+ /* if (!strcmp(account->username, sn))
icq_im_xstatus_request(od, sn); */
-
- status = purple_presence_get_active_status(account->presence);
- if (!status) return -EINVAL;
- title = purple_status_get_name(status);
- if (!title) return -EINVAL;
+
+ status = purple_presence_get_active_status(account->presence);
+ if (!status)
+ return -EINVAL;
+
+ title = purple_status_get_name(status);
+ if (!title)
+ return -EINVAL;
+
formatted_msg = purple_status_get_attr_string(status, "message");
- if (!formatted_msg) return -EINVAL;
- msg = purple_markup_strip_html(formatted_msg);
- if (!msg) return -EINVAL;
- len = strlen(fmt)-6+strlen(account->username)+strlen(title)+strlen(msg);
- statxml = (char*) g_malloc(len);
+ if (!formatted_msg)
+ return -EINVAL;
- snprintf(statxml, len, fmt,
- account->username, title, msg);
+ msg = purple_markup_strip_html(formatted_msg);
+ if (!msg)
+ return -EINVAL;
+
+ len = strlen(fmt) - 6 + strlen(account->username) + strlen(title) + strlen(msg);
+ statxml = g_malloc(len);
+
+ snprintf(statxml, len, fmt, account->username, title, msg);
purple_debug_misc("oscar", "X-Status AutoReply: %s, %s\n", formatted_msg, msg);
@@ -3021,8 +2062,8 @@ int icq_relay_xstatus(OscarData *od, const char *sn, const guchar *cookie)
byte_stream_put16(&bs, 0x0003);
byte_stream_putraw(&bs, plugindata, sizeof(plugindata));
byte_stream_putraw(&bs, (const guint8*)statxml, strlen(statxml));
-
- flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x000b, 0x0000, snacid, &bs, TRUE);
+
+ flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x000b, snacid, &bs, TRUE);
g_free(statxml);
g_free(msg);
@@ -3067,8 +2108,6 @@ snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *f
return error(od, conn, mod, frame, snac, bs);
else if (snac->subtype == 0x0005)
return aim_im_paraminfo(od, conn, mod, frame, snac, bs);
- else if (snac->subtype == 0x0006)
- return outgoingim(od, conn, mod, frame, snac, bs);
else if (snac->subtype == 0x0007)
return incomingim(od, conn, mod, frame, snac, bs);
else if (snac->subtype == 0x000a)
diff --git a/libpurple/protocols/oscar/family_icq.c b/libpurple/protocols/oscar/family_icq.c
index 3c796a299e..92008e8319 100644
--- a/libpurple/protocols/oscar/family_icq.c
+++ b/libpurple/protocols/oscar/family_icq.c
@@ -23,77 +23,104 @@
*
*/
+#include "encoding.h"
#include "oscar.h"
-#ifdef OLDSTYLE_ICQ_OFFLINEMSGS
-int aim_icq_reqofflinemsgs(OscarData *od)
-{
- FlapConnection *conn;
- ByteStream bs;
- aim_snacid_t snacid;
- int bslen;
-
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
- return -EINVAL;
-
- purple_debug_info("oscar", "Requesting offline messages\n");
-
- bslen = 2 + 4 + 2 + 2;
-
- byte_stream_new(&bs, 4 + bslen);
-
- snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
+#define AIM_ICQ_INFO_REQUEST 0x04b2
+#define AIM_ICQ_ALIAS_REQUEST 0x04ba
- /* For simplicity, don't bother using a tlvlist */
- byte_stream_put16(&bs, 0x0001);
- byte_stream_put16(&bs, bslen);
-
- byte_stream_putle16(&bs, bslen - 2);
- byte_stream_putuid(&bs, od);
- byte_stream_putle16(&bs, 0x003c); /* I command thee. */
- byte_stream_putle16(&bs, snacid); /* eh. */
-
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs);
+static
+int compare_icq_infos(gconstpointer a, gconstpointer b)
+{
+ const struct aim_icq_info* aa = a;
+ const guint16* bb = b;
+ return aa->reqid - *bb;
+}
- byte_stream_destroy(&bs);
+static void aim_icq_freeinfo(struct aim_icq_info *info) {
+ int i;
- return 0;
+ if (!info)
+ return;
+ g_free(info->nick);
+ g_free(info->first);
+ g_free(info->last);
+ g_free(info->email);
+ g_free(info->homecity);
+ g_free(info->homestate);
+ g_free(info->homephone);
+ g_free(info->homefax);
+ g_free(info->homeaddr);
+ g_free(info->mobile);
+ g_free(info->homezip);
+ g_free(info->personalwebpage);
+ if (info->email2)
+ for (i = 0; i < info->numaddresses; i++)
+ g_free(info->email2[i]);
+ g_free(info->email2);
+ g_free(info->workcity);
+ g_free(info->workstate);
+ g_free(info->workphone);
+ g_free(info->workfax);
+ g_free(info->workaddr);
+ g_free(info->workzip);
+ g_free(info->workcompany);
+ g_free(info->workdivision);
+ g_free(info->workposition);
+ g_free(info->workwebpage);
+ g_free(info->info);
+ g_free(info->status_note_title);
+ g_free(info->auth_request_reason);
}
-int aim_icq_ackofflinemsgs(OscarData *od)
+static
+int error(OscarData *od, aim_modsnac_t *error_snac, ByteStream *bs)
{
- ByteStream bs;
- FlapFrame *frame;
- aim_snacid_t snacid;
- int bslen;
-
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
- return -EINVAL;
-
- purple_debug_info("oscar", "Acknowledged receipt of offline messages\n");
-
- bslen = 2 + 4 + 2 + 2;
-
- byte_stream_new(&bs, 4 + bslen);
-
- snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
-
- /* For simplicity, don't bother using a tlvlist */
- byte_stream_put16(&bs, 0x0001);
- byte_stream_put16(&bs, bslen);
-
- byte_stream_putle16(&bs, bslen - 2);
- byte_stream_putuid(&bs, od);
- byte_stream_putle16(&bs, 0x003e); /* I command thee. */
- byte_stream_putle16(&bs, snacid); /* eh. */
-
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs);
-
- byte_stream_destroy(&bs);
+ aim_snac_t *original_snac = aim_remsnac(od, error_snac->id);
+ guint16 *request_type;
+ GSList *original_info_ptr;
+ struct aim_icq_info *original_info;
+ guint16 reason;
+ gchar *uin;
+
+ if (!original_snac || (original_snac->family != SNAC_FAMILY_ICQ) || !original_snac->data) {
+ purple_debug_misc("oscar", "icq: the original snac for the error packet was not found");
+ g_free(original_snac);
+ return 0;
+ }
+
+ request_type = original_snac->data;
+ original_info_ptr = g_slist_find_custom(od->icq_info, &original_snac->id, compare_icq_infos);
+ original_info = original_info_ptr->data;
+
+ if (!original_info_ptr) {
+ purple_debug_misc("oscar", "icq: the request info for the error packet was not found");
+ g_free(original_snac);
+ return 0;
+ }
+
+ reason = byte_stream_get16(bs);
+ uin = g_strdup_printf("%u", original_info->uin);
+ switch (*request_type) {
+ case AIM_ICQ_INFO_REQUEST:
+ oscar_user_info_display_error(od, reason, uin);
+ break;
+ case AIM_ICQ_ALIAS_REQUEST:
+ /* Couldn't retrieve an alias for the buddy requesting authorization; have to make do with UIN only. */
+ if (original_info->for_auth_request)
+ oscar_auth_recvrequest(od->gc, uin, NULL, original_info->auth_request_reason);
+ break;
+ default:
+ purple_debug_misc("oscar", "icq: got an error packet with unknown request type %u", *request_type);
+ break;
+ }
- return 0;
+ aim_icq_freeinfo(original_info);
+ od->icq_info = g_slist_remove(od->icq_info, original_info_ptr);
+ g_free(original_snac->data);
+ g_free(original_snac);
+ return 1;
}
-#endif /* OLDSTYLE_ICQ_OFFLINEMSGS */
int
aim_icq_setsecurity(OscarData *od, gboolean auth_required, gboolean webaware)
@@ -130,7 +157,7 @@ aim_icq_setsecurity(OscarData *od, gboolean auth_required, gboolean webaware)
byte_stream_putle8(&bs, 0x00);
byte_stream_putle8(&bs, !auth_required);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, snacid, &bs);
byte_stream_destroy(&bs);
@@ -180,7 +207,7 @@ int aim_icq_changepasswd(OscarData *od, const char *passwd)
byte_stream_putraw(&bs, (const guint8 *)passwd, passwdlen);
byte_stream_putle8(&bs, '\0');
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, snacid, &bs);
byte_stream_destroy(&bs);
@@ -194,6 +221,7 @@ int aim_icq_getallinfo(OscarData *od, const char *uin)
aim_snacid_t snacid;
int bslen;
struct aim_icq_info *info;
+ guint16 request_type = AIM_ICQ_INFO_REQUEST;
if (!uin || uin[0] < '0' || uin[0] > '9')
return -EINVAL;
@@ -205,7 +233,7 @@ int aim_icq_getallinfo(OscarData *od, const char *uin)
byte_stream_new(&bs, 4 + bslen);
- snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, &request_type, sizeof(request_type));
/* For simplicity, don't bother using a tlvlist */
byte_stream_put16(&bs, 0x0001);
@@ -215,10 +243,10 @@ int aim_icq_getallinfo(OscarData *od, const char *uin)
byte_stream_putuid(&bs, od);
byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
byte_stream_putle16(&bs, snacid); /* eh. */
- byte_stream_putle16(&bs, 0x04b2); /* shrug. */
+ byte_stream_putle16(&bs, request_type); /* shrug. */
byte_stream_putle32(&bs, atoi(uin));
- flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs, FALSE);
+ flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_ICQ, 0x0002, snacid, &bs, FALSE);
byte_stream_destroy(&bs);
@@ -226,19 +254,19 @@ int aim_icq_getallinfo(OscarData *od, const char *uin)
info = (struct aim_icq_info *)g_new0(struct aim_icq_info, 1);
info->reqid = snacid;
info->uin = atoi(uin);
- info->next = od->icq_info;
- od->icq_info = info;
+ od->icq_info = g_slist_prepend(od->icq_info, info);
return 0;
}
-int aim_icq_getalias(OscarData *od, const char *uin)
+int aim_icq_getalias(OscarData *od, const char *uin, gboolean for_auth_request, char *auth_request_reason)
{
FlapConnection *conn;
ByteStream bs;
aim_snacid_t snacid;
int bslen;
struct aim_icq_info *info;
+ guint16 request_type = AIM_ICQ_ALIAS_REQUEST;
if (!uin || uin[0] < '0' || uin[0] > '9')
return -EINVAL;
@@ -252,7 +280,7 @@ int aim_icq_getalias(OscarData *od, const char *uin)
byte_stream_new(&bs, 4 + bslen);
- snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, &request_type, sizeof(request_type));
/* For simplicity, don't bother using a tlvlist */
byte_stream_put16(&bs, 0x0001);
@@ -262,10 +290,10 @@ int aim_icq_getalias(OscarData *od, const char *uin)
byte_stream_putuid(&bs, od);
byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
byte_stream_putle16(&bs, snacid); /* eh. */
- byte_stream_putle16(&bs, 0x04ba); /* shrug. */
+ byte_stream_putle16(&bs, request_type); /* shrug. */
byte_stream_putle32(&bs, atoi(uin));
- flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs, FALSE);
+ flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_ICQ, 0x0002, snacid, &bs, FALSE);
byte_stream_destroy(&bs);
@@ -273,88 +301,12 @@ int aim_icq_getalias(OscarData *od, const char *uin)
info = (struct aim_icq_info *)g_new0(struct aim_icq_info, 1);
info->reqid = snacid;
info->uin = atoi(uin);
- info->next = od->icq_info;
- od->icq_info = info;
-
- return 0;
-}
-
-int aim_icq_getsimpleinfo(OscarData *od, const char *uin)
-{
- FlapConnection *conn;
- ByteStream bs;
- aim_snacid_t snacid;
- int bslen;
-
- if (!uin || uin[0] < '0' || uin[0] > '9')
- return -EINVAL;
-
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
- return -EINVAL;
-
- bslen = 2 + 4 + 2 + 2 + 2 + 4;
-
- byte_stream_new(&bs, 4 + bslen);
-
- snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
-
- /* For simplicity, don't bother using a tlvlist */
- byte_stream_put16(&bs, 0x0001);
- byte_stream_put16(&bs, bslen);
-
- byte_stream_putle16(&bs, bslen - 2);
- byte_stream_putuid(&bs, od);
- byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
- byte_stream_putle16(&bs, snacid); /* eh. */
- byte_stream_putle16(&bs, 0x051f); /* shrug. */
- byte_stream_putle32(&bs, atoi(uin));
-
- flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs, FALSE);
-
- byte_stream_destroy(&bs);
-
- return 0;
-}
-
-#if 0
-int aim_icq_sendxmlreq(OscarData *od, const char *xml)
-{
- FlapConnection *conn;
- ByteStream bs;
- aim_snacid_t snacid;
- int bslen;
-
- if (!xml || !strlen(xml))
- return -EINVAL;
-
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
- return -EINVAL;
-
- bslen = 2 + 10 + 2 + strlen(xml) + 1;
-
- byte_stream_new(&bs, 4 + bslen);
-
- snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
-
- /* For simplicity, don't bother using a tlvlist */
- byte_stream_put16(&bs, 0x0001);
- byte_stream_put16(&bs, bslen);
-
- byte_stream_putle16(&bs, bslen - 2);
- byte_stream_putuid(&bs, od);
- byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
- byte_stream_putle16(&bs, snacid); /* eh. */
- byte_stream_putle16(&bs, 0x0998); /* shrug. */
- byte_stream_putle16(&bs, strlen(xml) + 1);
- byte_stream_putraw(&bs, (guint8 *)xml, strlen(xml) + 1);
-
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs);
-
- byte_stream_destroy(&bs);
+ info->for_auth_request = for_auth_request;
+ info->auth_request_reason = g_strdup(auth_request_reason);
+ od->icq_info = g_slist_prepend(od->icq_info, info);
return 0;
}
-#endif
/*
* Send an SMS message. This is the non-US way. The US-way is to IM
@@ -446,7 +398,7 @@ int aim_icq_sendsms(OscarData *od, const char *name, const char *msg, const char
byte_stream_putstr(&bs, xml);
byte_stream_put8(&bs, 0x00);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, snacid, &bs);
byte_stream_destroy(&bs);
@@ -456,49 +408,35 @@ int aim_icq_sendsms(OscarData *od, const char *name, const char *msg, const char
return 0;
}
-static void aim_icq_freeinfo(struct aim_icq_info *info) {
- int i;
-
- if (!info)
- return;
- g_free(info->nick);
- g_free(info->first);
- g_free(info->last);
- g_free(info->email);
- g_free(info->homecity);
- g_free(info->homestate);
- g_free(info->homephone);
- g_free(info->homefax);
- g_free(info->homeaddr);
- g_free(info->mobile);
- g_free(info->homezip);
- g_free(info->personalwebpage);
- if (info->email2)
- for (i = 0; i < info->numaddresses; i++)
- g_free(info->email2[i]);
- g_free(info->email2);
- g_free(info->workcity);
- g_free(info->workstate);
- g_free(info->workphone);
- g_free(info->workfax);
- g_free(info->workaddr);
- g_free(info->workzip);
- g_free(info->workcompany);
- g_free(info->workdivision);
- g_free(info->workposition);
- g_free(info->workwebpage);
- g_free(info->info);
- g_free(info->status_note_title);
- g_free(info);
+static int
+gotalias(OscarData *od, struct aim_icq_info *info)
+{
+ PurpleConnection *gc = od->gc;
+ PurpleAccount *account = purple_connection_get_account(gc);
+ gchar who[16], *utf8;
+ PurpleBuddy *b;
+
+ if (info->nick[0] && (utf8 = oscar_utf8_try_convert(account, od, info->nick))) {
+ if (info->for_auth_request) {
+ oscar_auth_recvrequest(gc, g_strdup_printf("%u", info->uin), utf8, info->auth_request_reason);
+ } else {
+ g_snprintf(who, sizeof(who), "%u", info->uin);
+ serv_got_alias(gc, who, utf8);
+ if ((b = purple_find_buddy(account, who))) {
+ purple_blist_node_set_string((PurpleBlistNode*)b, "servernick", utf8);
+ }
+ g_free(utf8);
+ }
+ }
+ return 1;
}
/**
* Subtype 0x0003 - Response to SNAC_FAMILY_ICQ/0x002, contains an ICQesque packet.
*/
static int
-icqresponse(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
+icqresponse(OscarData *od, aim_modsnac_t *snac, ByteStream *bs)
{
- int ret = 0;
GSList *tlvlist;
aim_tlv_t *datatlv;
ByteStream qbs;
@@ -520,53 +458,23 @@ icqresponse(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *f
purple_debug_misc("oscar", "icq response: %d bytes, %u, 0x%04x, 0x%04x\n", cmdlen, ouruin, cmd, reqid);
- if (cmd == 0x0041) { /* offline message */
-#ifdef OLDSTYLE_ICQ_OFFLINEMSGS
- struct aim_icq_offlinemsg msg;
- aim_rxcallback_t userfunc;
-
- memset(&msg, 0, sizeof(msg));
-
- msg.sender = byte_stream_getle32(&qbs);
- msg.year = byte_stream_getle16(&qbs);
- msg.month = byte_stream_getle8(&qbs);
- msg.day = byte_stream_getle8(&qbs);
- msg.hour = byte_stream_getle8(&qbs);
- msg.minute = byte_stream_getle8(&qbs);
- msg.type = byte_stream_getle8(&qbs);
- msg.flags = byte_stream_getle8(&qbs);
- msg.msglen = byte_stream_getle16(&qbs);
- msg.msg = byte_stream_getstr(&qbs, msg.msglen);
-
- if ((userfunc = aim_callhandler(od, SNAC_FAMILY_ICQ, SNAC_SUBTYPE_ICQ_OFFLINEMSG)))
- ret = userfunc(od, conn, frame, &msg);
-
- g_free(msg.msg);
-
- } else if (cmd == 0x0042) {
- aim_rxcallback_t userfunc;
-
- if ((userfunc = aim_callhandler(od, SNAC_FAMILY_ICQ, SNAC_SUBTYPE_ICQ_OFFLINEMSGCOMPLETE)))
- ret = userfunc(od, conn, frame);
-#endif /* OLDSTYLE_ICQ_OFFLINEMSGS */
-
- } else if (cmd == 0x07da) { /* information */
+ if (cmd == 0x07da) { /* information */
guint16 subtype;
+ GSList *info_ptr;
struct aim_icq_info *info;
- aim_rxcallback_t userfunc;
subtype = byte_stream_getle16(&qbs);
byte_stream_advance(&qbs, 1); /* 0x0a */
/* find other data from the same request */
- for (info = od->icq_info; info && (info->reqid != reqid); info = info->next);
- if (!info) {
- info = (struct aim_icq_info *)g_new0(struct aim_icq_info, 1);
- info->reqid = reqid;
- info->next = od->icq_info;
- od->icq_info = info;
+ info_ptr = g_slist_find_custom(od->icq_info, &reqid, compare_icq_infos);
+ if (!info_ptr) {
+ struct aim_icq_info *new_info = (struct aim_icq_info *)g_new0(struct aim_icq_info, 1);
+ new_info->reqid = reqid;
+ info_ptr = od->icq_info = g_slist_prepend(od->icq_info, new_info);
}
+ info = info_ptr->data;
switch (subtype) {
case 0x00a0: { /* hide ip status */
/* nothing */
@@ -818,10 +726,9 @@ icqresponse(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *f
memcpy(&info->icbm_cookie, cookie, 8);
- info->next = od->icq_info;
- od->icq_info = info;
+ od->icq_info = g_slist_prepend(od->icq_info, info);
- flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs, FALSE);
+ flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x0006, snacid, &bs, FALSE);
byte_stream_destroy(&bs);
}
@@ -834,35 +741,28 @@ icqresponse(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *f
if (!(snac->flags & 0x0001)) {
if (subtype != 0x0104)
- if ((userfunc = aim_callhandler(od, SNAC_FAMILY_ICQ, SNAC_SUBTYPE_ICQ_INFO)))
- ret = userfunc(od, conn, frame, info);
+ oscar_user_info_display_icq(od, info);
if (info->uin && info->nick)
- if ((userfunc = aim_callhandler(od, SNAC_FAMILY_ICQ, SNAC_SUBTYPE_ICQ_ALIAS)))
- ret = userfunc(od, conn, frame, info);
-
- if (od->icq_info == info) {
- od->icq_info = info->next;
- } else {
- struct aim_icq_info *cur;
- for (cur=od->icq_info; (cur->next && (cur->next!=info)); cur=cur->next);
- if (cur->next)
- cur->next = cur->next->next;
- }
+ gotalias(od, info);
+
aim_icq_freeinfo(info);
+ od->icq_info = g_slist_remove(od->icq_info, info);
}
}
aim_tlvlist_free(tlvlist);
- return ret;
+ return 1;
}
static int
snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
{
- if (snac->subtype == 0x0003)
- return icqresponse(od, conn, mod, frame, snac, bs);
+ if (snac->subtype == 0x0001)
+ return error(od, snac, bs);
+ else if (snac->subtype == 0x0003)
+ return icqresponse(od, snac, bs);
return 0;
}
@@ -870,15 +770,10 @@ snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *f
static void
icq_shutdown(OscarData *od, aim_module_t *mod)
{
- struct aim_icq_info *del;
-
- while (od->icq_info) {
- del = od->icq_info;
- od->icq_info = od->icq_info->next;
- aim_icq_freeinfo(del);
- }
-
- return;
+ GSList *cur;
+ for (cur = od->icq_info; cur; cur = cur->next)
+ aim_icq_freeinfo(cur->data);
+ g_slist_free(od->icq_info);
}
int
diff --git a/libpurple/protocols/oscar/family_invite.c b/libpurple/protocols/oscar/family_invite.c
deleted file mode 100644
index bd9cbd25fb..0000000000
--- a/libpurple/protocols/oscar/family_invite.c
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Purple's oscar protocol plugin
- * This file is the legal property of its developers.
- * Please see the AUTHORS file distributed alongside this file.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
-*/
-
-/*
- * Family 0x0006 - This isn't really ever used by anyone anymore.
- *
- * Once upon a time, there used to be a menu item in AIM clients that
- * said something like "Invite a friend to use AIM..." and then it would
- * ask for an email address and it would sent a mail to them saying
- * how perfectly wonderful the AIM service is and why you should use it
- * and click here if you hate the person who sent this to you and want to
- * complain and yell at them in a small box with pretty fonts.
- *
- * I could've sworn libfaim had this implemented once, a long long time ago,
- * but I can't find it.
- *
- * I'm mainly adding this so that I can keep advertising that we support
- * group 6, even though we don't.
- *
- */
-
-#include "oscar.h"
-
-int invite_modfirst(OscarData *od, aim_module_t *mod)
-{
-
- mod->family = SNAC_FAMILY_INVITE;
- mod->version = 0x0001;
- mod->toolid = 0x0110;
- mod->toolversion = 0x0629;
- mod->flags = 0;
- strncpy(mod->name, "invite", sizeof(mod->name));
- mod->snachandler = NULL;
-
- return 0;
-}
diff --git a/libpurple/protocols/oscar/family_locate.c b/libpurple/protocols/oscar/family_locate.c
index eebf72c96c..8296aaca59 100644
--- a/libpurple/protocols/oscar/family_locate.c
+++ b/libpurple/protocols/oscar/family_locate.c
@@ -245,6 +245,10 @@ static const struct {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ {OSCAR_CAPABILITY_HTML_MSGS,
+ {0x01, 0x38, 0xca, 0x7b, 0x76, 0x9a, 0x49, 0x15,
+ 0x88, 0xf2, 0x13, 0xfc, 0x00, 0x97, 0x9e, 0xa8}},
+
{OSCAR_CAPABILITY_LAST,
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
@@ -583,7 +587,7 @@ aim_locate_getcaps(OscarData *od, ByteStream *bs, int len)
guint64 flags = 0;
int offset;
- for (offset = 0; byte_stream_empty(bs) && (offset < len); offset += 0x10) {
+ for (offset = 0; byte_stream_bytes_left(bs) && (offset < len); offset += 0x10) {
guint8 *cap;
int i, identified;
@@ -617,7 +621,7 @@ aim_receive_custom_icon(OscarData *od, ByteStream *bs, int len)
int offset;
const char *result = NULL;
- for (offset = 0; byte_stream_empty(bs) && (offset < len); offset += 0x10) {
+ for (offset = 0; byte_stream_bytes_left(bs) && (offset < len); offset += 0x10) {
/* check wheather this capability is a custom user icon */
guint8 *cap;
int i;
@@ -643,7 +647,7 @@ aim_locate_getcaps_short(OscarData *od, ByteStream *bs, int len)
guint64 flags = 0;
int offset;
- for (offset = 0; byte_stream_empty(bs) && (offset < len); offset += 0x02) {
+ for (offset = 0; byte_stream_bytes_left(bs) && (offset < len); offset += 0x02) {
guint8 *cap;
int i, identified;
@@ -674,16 +678,13 @@ byte_stream_putcaps(ByteStream *bs, guint64 caps)
if (!bs)
return -EINVAL;
- for (i = 0; byte_stream_empty(bs); i++) {
-
+ for (i = 0; byte_stream_bytes_left(bs); i++) {
if (aim_caps[i].flag == OSCAR_CAPABILITY_LAST)
break;
if (caps & aim_caps[i].flag)
byte_stream_putraw(bs, aim_caps[i].data, 0x10);
-
}
-
return 0;
}
@@ -804,7 +805,7 @@ aim_info_extract(OscarData *od, ByteStream *bs, aim_userinfo_t *outinfo)
type = byte_stream_get16(bs);
length = byte_stream_get16(bs);
curpos = byte_stream_curpos(bs);
- endpos = curpos + MIN(length, byte_stream_empty(bs));
+ endpos = curpos + MIN(length, byte_stream_bytes_left(bs));
if (type == 0x0001) {
/*
@@ -1010,7 +1011,7 @@ aim_info_extract(OscarData *od, ByteStream *bs, aim_userinfo_t *outinfo)
number2 = byte_stream_get8(bs);
length2 = byte_stream_get8(bs);
- endpos2 = byte_stream_curpos(bs) + MIN(length2, byte_stream_empty(bs));
+ endpos2 = byte_stream_curpos(bs) + MIN(length2, byte_stream_bytes_left(bs));
switch (type2) {
case 0x0000: { /* This is an official buddy icon? */
@@ -1165,73 +1166,18 @@ aim_info_extract(OscarData *od, ByteStream *bs, aim_userinfo_t *outinfo)
return 0;
}
-/* Apparently, this is never called.
- * If you activate it, figure out a way to know what mood to pass to
- * aim_tlvlist_add_caps() below. --rlaager */
-#if 0
-/*
- * Inverse of aim_info_extract()
- */
-int
-aim_putuserinfo(ByteStream *bs, aim_userinfo_t *info)
-{
- GSList *tlvlist = NULL;
-
- if (!bs || !info)
- return -EINVAL;
-
- byte_stream_put8(bs, strlen(info->bn));
- byte_stream_putstr(bs, info->bn);
-
- byte_stream_put16(bs, info->warnlevel);
-
- if (info->present & AIM_USERINFO_PRESENT_FLAGS)
- aim_tlvlist_add_16(&tlvlist, 0x0001, info->flags);
- if (info->present & AIM_USERINFO_PRESENT_MEMBERSINCE)
- aim_tlvlist_add_32(&tlvlist, 0x0002, info->membersince);
- if (info->present & AIM_USERINFO_PRESENT_ONLINESINCE)
- aim_tlvlist_add_32(&tlvlist, 0x0003, info->onlinesince);
- if (info->present & AIM_USERINFO_PRESENT_IDLE)
- aim_tlvlist_add_16(&tlvlist, 0x0004, info->idletime);
-
-/* XXX - So, ICQ_OSCAR_SUPPORT is never defined anywhere... */
-#ifdef ICQ_OSCAR_SUPPORT
- if (atoi(info->bn) != 0) {
- if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS)
- aim_tlvlist_add_16(&tlvlist, 0x0006, info->icqinfo.status);
- if (info->present & AIM_USERINFO_PRESENT_ICQIPADDR)
- aim_tlvlist_add_32(&tlvlist, 0x000a, info->icqinfo.ipaddr);
- }
-#endif
-
- if (info->present & AIM_USERINFO_PRESENT_CAPABILITIES) {
- aim_tlvlist_add_caps(&tlvlist, 0x000d, info->capabilities, NULL);
- }
-
- if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN)
- aim_tlvlist_add_32(&tlvlist, (guint16)((info->flags & AIM_FLAG_AOL) ? 0x0010 : 0x000f), info->sessionlen);
-
- byte_stream_put16(bs, aim_tlvlist_count(tlvlist));
- aim_tlvlist_write(bs, &tlvlist);
- aim_tlvlist_free(tlvlist);
-
- return 0;
-}
-#endif
-
/*
* Subtype 0x0001
*/
static int
error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
{
- int ret = 0;
- aim_rxcallback_t userfunc;
aim_snac_t *snac2;
guint16 reason;
char *bn;
- if (!(snac2 = aim_remsnac(od, snac->id))) {
+ snac2 = aim_remsnac(od, snac->id);
+ if (!snac2) {
purple_debug_misc("oscar", "locate error: received response from unknown request!\n");
return 0;
}
@@ -1243,7 +1189,8 @@ error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame,
return 0;
}
- if (!(bn = snac2->data)) {
+ bn = snac2->data;
+ if (!bn) {
purple_debug_misc("oscar", "locate error: received response from request without a buddy name!\n");
g_free(snac2);
return 0;
@@ -1251,15 +1198,12 @@ error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame,
reason = byte_stream_get16(bs);
- /* Notify the user that we do not have info for this buddy */
- if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
- ret = userfunc(od, conn, frame, reason, bn);
+ oscar_user_info_display_error(od, reason, bn);
- if (snac2)
- g_free(snac2->data);
+ g_free(snac2->data);
g_free(snac2);
- return ret;
+ return 1;
}
/*
@@ -1389,7 +1333,7 @@ aim_locate_setprofile(OscarData *od,
aim_tlvlist_write(&bs, &tlvlist);
aim_tlvlist_free(tlvlist);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0004, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0004, snacid, &bs);
byte_stream_destroy(&bs);
@@ -1423,41 +1367,7 @@ aim_locate_setcaps(OscarData *od, guint64 caps)
aim_tlvlist_write(&bs, &tlvlist);
aim_tlvlist_free(tlvlist);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0004, 0x0000, snacid, &bs);
-
- byte_stream_destroy(&bs);
-
- return 0;
-}
-
-/*
- * Subtype 0x0005 - Request info of another AIM user.
- *
- * @param bn The buddy name whose info you wish to request.
- * @param infotype The type of info you wish to request.
- * 0x0001 - Info/profile
- * 0x0003 - Away message
- * 0x0004 - Capabilities
- */
-int
-aim_locate_getinfo(OscarData *od, const char *bn, guint16 infotype)
-{
- FlapConnection *conn;
- ByteStream bs;
- aim_snacid_t snacid;
-
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)) || !bn)
- return -EINVAL;
-
- byte_stream_new(&bs, 2+1+strlen(bn));
-
- snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0005, 0x0000, NULL, 0);
-
- byte_stream_put16(&bs, infotype);
- byte_stream_put8(&bs, strlen(bn));
- byte_stream_putstr(&bs, bn);
-
- flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0005, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0004, snacid, &bs);
byte_stream_destroy(&bs);
@@ -1469,7 +1379,6 @@ static int
userinfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
{
int ret = 0;
- aim_rxcallback_t userfunc;
aim_userinfo_t *userinfo, *userinfo2;
GSList *tlvlist;
aim_tlv_t *tlv = NULL;
@@ -1521,140 +1430,12 @@ userinfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *fram
g_free(userinfo);
/* Show the info to the user */
- if (userinfo2 != NULL && ((userfunc = aim_callhandler(od, snac->family, snac->subtype))))
- ret = userfunc(od, conn, frame, userinfo2);
+ oscar_user_info_display_aim(od, userinfo2);
return ret;
}
/*
- * Subtype 0x0009 - Set directory profile data.
- *
- * This is not the same as aim_location_setprofile!
- * privacy: 1 to allow searching, 0 to disallow.
- *
- */
-int aim_locate_setdirinfo(OscarData *od, const char *first, const char *middle, const char *last, const char *maiden, const char *nickname, const char *street, const char *city, const char *state, const char *zip, int country, guint16 privacy)
-{
- FlapConnection *conn;
- ByteStream bs;
- aim_snacid_t snacid;
- GSList *tlvlist = NULL;
-
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)))
- return -EINVAL;
-
- aim_tlvlist_add_16(&tlvlist, 0x000a, privacy);
-
- if (first)
- aim_tlvlist_add_str(&tlvlist, 0x0001, first);
- if (last)
- aim_tlvlist_add_str(&tlvlist, 0x0002, last);
- if (middle)
- aim_tlvlist_add_str(&tlvlist, 0x0003, middle);
- if (maiden)
- aim_tlvlist_add_str(&tlvlist, 0x0004, maiden);
-
- if (state)
- aim_tlvlist_add_str(&tlvlist, 0x0007, state);
- if (city)
- aim_tlvlist_add_str(&tlvlist, 0x0008, city);
-
- if (nickname)
- aim_tlvlist_add_str(&tlvlist, 0x000c, nickname);
- if (zip)
- aim_tlvlist_add_str(&tlvlist, 0x000d, zip);
-
- if (street)
- aim_tlvlist_add_str(&tlvlist, 0x0021, street);
-
- byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
-
- snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0009, 0x0000, NULL, 0);
-
- aim_tlvlist_write(&bs, &tlvlist);
- aim_tlvlist_free(tlvlist);
-
- flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0009, 0x0000, snacid, &bs);
-
- byte_stream_destroy(&bs);
-
- return 0;
-}
-
-/*
- * Subtype 0x000b - Huh? What is this?
- */
-int aim_locate_000b(OscarData *od, const char *bn)
-{
- FlapConnection *conn;
- ByteStream bs;
- aim_snacid_t snacid;
-
- return -EINVAL;
-
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)) || !bn)
- return -EINVAL;
-
- byte_stream_new(&bs, 1+strlen(bn));
-
- snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x000b, 0x0000, NULL, 0);
-
- byte_stream_put8(&bs, strlen(bn));
- byte_stream_putstr(&bs, bn);
-
- flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x000b, 0x0000, snacid, &bs);
-
- byte_stream_destroy(&bs);
-
- return 0;
-}
-
-/*
- * Subtype 0x000f
- *
- * XXX pass these in better
- *
- */
-int
-aim_locate_setinterests(OscarData *od, const char *interest1, const char *interest2, const char *interest3, const char *interest4, const char *interest5, guint16 privacy)
-{
- FlapConnection *conn;
- ByteStream bs;
- aim_snacid_t snacid;
- GSList *tlvlist = NULL;
-
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)))
- return -EINVAL;
-
- /* ?? privacy ?? */
- aim_tlvlist_add_16(&tlvlist, 0x000a, privacy);
-
- if (interest1)
- aim_tlvlist_add_str(&tlvlist, 0x0000b, interest1);
- if (interest2)
- aim_tlvlist_add_str(&tlvlist, 0x0000b, interest2);
- if (interest3)
- aim_tlvlist_add_str(&tlvlist, 0x0000b, interest3);
- if (interest4)
- aim_tlvlist_add_str(&tlvlist, 0x0000b, interest4);
- if (interest5)
- aim_tlvlist_add_str(&tlvlist, 0x0000b, interest5);
-
- byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
-
- snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x000f, 0x0000, NULL, 0);
-
- aim_tlvlist_write(&bs, &tlvlist);
- aim_tlvlist_free(tlvlist);
-
- flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x000f, 0x0000, snacid, &bs);
-
- byte_stream_destroy(&bs);
- return 0;
-}
-
-/*
* Subtype 0x0015 - Request the info of a user using the short method. This is
* what iChat uses. It normally is VERY leniently rate limited.
*
@@ -1682,7 +1463,7 @@ aim_locate_getinfoshort(OscarData *od, const char *bn, guint32 flags)
byte_stream_putstr(&bs, bn);
snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0015, 0x0000, bn, strlen(bn)+1);
- flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_LOCATE, 0x0015, 0x0000, snacid, &bs, FALSE);
+ flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_LOCATE, 0x0015, snacid, &bs, FALSE);
byte_stream_destroy(&bs);
@@ -1730,15 +1511,6 @@ locate_modfirst(OscarData *od, aim_module_t *mod)
return 0;
}
-#if 0 //rlaager
-const char* aim_get_custom_icon_mood(gint32 no)
-{
- if (no >= G_N_ELEMENTS(aim_custom_icons) || no < 1)
- return NULL;
- return aim_custom_icons[no].mood.mood;
-}
-#endif
-
const char*
icq_get_custom_icon_description(const char *mood)
{
diff --git a/libpurple/protocols/oscar/family_odir.c b/libpurple/protocols/oscar/family_odir.c
deleted file mode 100644
index be1638fb39..0000000000
--- a/libpurple/protocols/oscar/family_odir.c
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Purple's oscar protocol plugin
- * This file is the legal property of its developers.
- * Please see the AUTHORS file distributed alongside this file.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
-*/
-
-/*
- * Family 0x000f - Newer Search Method
- *
- * Used for searching for other AIM users by email address, name,
- * location, commmon interests, and a few other similar things.
- *
- */
-
-#include "oscar.h"
-
-/**
- * Subtype 0x0002 - Submit a User Search Request
- *
- * Search for an AIM buddy based on their email address.
- *
- * @param od The oscar session.
- * @param region Should be "us-ascii" unless you know what you're doing.
- * @param email The email address you want to search for.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-int aim_odir_email(OscarData *od, const char *region, const char *email)
-{
- FlapConnection *conn;
- ByteStream bs;
- aim_snacid_t snacid;
- GSList *tlvlist = NULL;
-
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ODIR)) || !region || !email)
- return -EINVAL;
-
- /* Create a TLV chain, write it to the outgoing frame, then free the chain */
- aim_tlvlist_add_str(&tlvlist, 0x001c, region);
- aim_tlvlist_add_16(&tlvlist, 0x000a, 0x0001); /* Type of search */
- aim_tlvlist_add_str(&tlvlist, 0x0005, email);
-
- byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
-
- aim_tlvlist_write(&bs, &tlvlist);
- aim_tlvlist_free(tlvlist);
-
- snacid = aim_cachesnac(od, SNAC_FAMILY_ODIR, 0x0002, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ODIR, 0x0002, 0x0000, snacid, &bs);
-
- byte_stream_destroy(&bs);
-
- return 0;
-}
-
-
-/**
- * Subtype 0x0002 - Submit a User Search Request
- *
- * Search for an AIM buddy based on various info
- * about the person.
- *
- * @param od The oscar session.
- * @param region Should be "us-ascii" unless you know what you're doing.
- * @param first The first name of the person you want to search for.
- * @param middle The middle name of the person you want to search for.
- * @param last The last name of the person you want to search for.
- * @param maiden The maiden name of the person you want to search for.
- * @param nick The nick name of the person you want to search for.
- * @param city The city where the person you want to search for resides.
- * @param state The state where the person you want to search for resides.
- * @param country The country where the person you want to search for resides.
- * @param zip The zip code where the person you want to search for resides.
- * @param address The street address where the person you want to seach for resides.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-int aim_odir_name(OscarData *od, const char *region, const char *first, const char *middle, const char *last, const char *maiden, const char *nick, const char *city, const char *state, const char *country, const char *zip, const char *address)
-{
- FlapConnection *conn;
- ByteStream bs;
- aim_snacid_t snacid;
- GSList *tlvlist = NULL;
-
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ODIR)) || !region)
- return -EINVAL;
-
- /* Create a TLV chain, write it to the outgoing frame, then free the chain */
- aim_tlvlist_add_str(&tlvlist, 0x001c, region);
- aim_tlvlist_add_16(&tlvlist, 0x000a, 0x0000); /* Type of search */
- if (first)
- aim_tlvlist_add_str(&tlvlist, 0x0001, first);
- if (last)
- aim_tlvlist_add_str(&tlvlist, 0x0002, last);
- if (middle)
- aim_tlvlist_add_str(&tlvlist, 0x0003, middle);
- if (maiden)
- aim_tlvlist_add_str(&tlvlist, 0x0004, maiden);
- if (country)
- aim_tlvlist_add_str(&tlvlist, 0x0006, country);
- if (state)
- aim_tlvlist_add_str(&tlvlist, 0x0007, state);
- if (city)
- aim_tlvlist_add_str(&tlvlist, 0x0008, city);
- if (nick)
- aim_tlvlist_add_str(&tlvlist, 0x000c, nick);
- if (zip)
- aim_tlvlist_add_str(&tlvlist, 0x000d, zip);
- if (address)
- aim_tlvlist_add_str(&tlvlist, 0x0021, address);
-
- byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
-
- aim_tlvlist_write(&bs, &tlvlist);
- aim_tlvlist_free(tlvlist);
-
- snacid = aim_cachesnac(od, SNAC_FAMILY_ODIR, 0x0002, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ODIR, 0x0002, 0x0000, snacid, &bs);
-
- byte_stream_destroy(&bs);
-
- return 0;
-}
-
-
-/**
- * Subtype 0x0002 - Submit a User Search Request
- *
- * @param od The oscar session.
- * @param interest1 An interest you want to search for.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-int aim_odir_interest(OscarData *od, const char *region, const char *interest)
-{
- FlapConnection *conn;
- ByteStream bs;
- aim_snacid_t snacid;
- GSList *tlvlist = NULL;
-
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ODIR)) || !region)
- return -EINVAL;
-
- /* Create a TLV chain, write it to the outgoing frame, then free the chain */
- aim_tlvlist_add_str(&tlvlist, 0x001c, region);
- aim_tlvlist_add_16(&tlvlist, 0x000a, 0x0001); /* Type of search */
- if (interest)
- aim_tlvlist_add_str(&tlvlist, 0x0001, interest);
-
- byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
-
- aim_tlvlist_write(&bs, &tlvlist);
- aim_tlvlist_free(tlvlist);
-
- snacid = aim_cachesnac(od, SNAC_FAMILY_ODIR, 0x0002, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ODIR, 0x0002, 0x0000, snacid, &bs);
-
- byte_stream_destroy(&bs);
-
- return 0;
-}
-
-
-/**
- * Subtype 0x0003 - Receive Reply From a User Search
- *
- */
-static int parseresults(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
-{
- int ret = 0;
- aim_rxcallback_t userfunc;
- guint16 tmp, numresults;
- struct aim_odir *results = NULL;
-
- tmp = byte_stream_get16(bs); /* Unknown */
- tmp = byte_stream_get16(bs); /* Unknown */
- byte_stream_advance(bs, tmp);
-
- numresults = byte_stream_get16(bs); /* Number of results to follow */
-
- /* Allocate a linked list, 1 node per result */
- while (numresults) {
- struct aim_odir *new;
- GSList *tlvlist = aim_tlvlist_readnum(bs, byte_stream_get16(bs));
- new = (struct aim_odir *)g_malloc(sizeof(struct aim_odir));
- new->first = aim_tlv_getstr(tlvlist, 0x0001, 1);
- new->last = aim_tlv_getstr(tlvlist, 0x0002, 1);
- new->middle = aim_tlv_getstr(tlvlist, 0x0003, 1);
- new->maiden = aim_tlv_getstr(tlvlist, 0x0004, 1);
- new->email = aim_tlv_getstr(tlvlist, 0x0005, 1);
- new->country = aim_tlv_getstr(tlvlist, 0x0006, 1);
- new->state = aim_tlv_getstr(tlvlist, 0x0007, 1);
- new->city = aim_tlv_getstr(tlvlist, 0x0008, 1);
- new->bn = aim_tlv_getstr(tlvlist, 0x0009, 1);
- new->interest = aim_tlv_getstr(tlvlist, 0x000b, 1);
- new->nick = aim_tlv_getstr(tlvlist, 0x000c, 1);
- new->zip = aim_tlv_getstr(tlvlist, 0x000d, 1);
- new->region = aim_tlv_getstr(tlvlist, 0x001c, 1);
- new->address = aim_tlv_getstr(tlvlist, 0x0021, 1);
- new->next = results;
- results = new;
- numresults--;
- }
-
- if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
- ret = userfunc(od, conn, frame, results);
-
- /* Now free everything from above */
- while (results) {
- struct aim_odir *del = results;
- results = results->next;
- g_free(del->first);
- g_free(del->last);
- g_free(del->middle);
- g_free(del->maiden);
- g_free(del->email);
- g_free(del->country);
- g_free(del->state);
- g_free(del->city);
- g_free(del->bn);
- g_free(del->interest);
- g_free(del->nick);
- g_free(del->zip);
- g_free(del->region);
- g_free(del->address);
- g_free(del);
- }
-
- return ret;
-}
-
-static int
-snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
-{
- if (snac->subtype == 0x0003)
- return parseresults(od, conn, mod, frame, snac, bs);
-
- return 0;
-}
-
-int
-odir_modfirst(OscarData *od, aim_module_t *mod)
-{
- mod->family = SNAC_FAMILY_ODIR;
- mod->version = 0x0001;
- mod->toolid = 0x0010;
- mod->toolversion = 0x0629;
- mod->flags = 0;
- strncpy(mod->name, "odir", sizeof(mod->name));
- mod->snachandler = snachandler;
-
- return 0;
-}
diff --git a/libpurple/protocols/oscar/family_oservice.c b/libpurple/protocols/oscar/family_oservice.c
index 9b554aab0e..b566642289 100644
--- a/libpurple/protocols/oscar/family_oservice.c
+++ b/libpurple/protocols/oscar/family_oservice.c
@@ -73,7 +73,7 @@ aim_srv_clientready(OscarData *od, FlapConnection *conn)
}
snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0002, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0002, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0002, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -97,7 +97,7 @@ hostonline(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *fr
{
int group;
- while (byte_stream_empty(bs))
+ while (byte_stream_bytes_left(bs))
{
group = byte_stream_get16(bs);
conn->groups = g_slist_prepend(conn->groups, GUINT_TO_POINTER(group));
@@ -141,7 +141,7 @@ aim_srv_requestnew(OscarData *od, guint16 serviceid)
aim_tlvlist_free(tlvlist);
snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0004, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0004, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0004, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -187,7 +187,7 @@ aim_chat_join(OscarData *od, guint16 exchange, const char *roomname, guint16 ins
aim_tlvlist_free(tlvlist);
snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0004, 0x0000, &csi, sizeof(csi));
- flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0004, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0004, snacid, &bs);
byte_stream_destroy(&bs);
@@ -444,30 +444,7 @@ aim_srv_rates_addparam(OscarData *od, FlapConnection *conn)
}
snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0008, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0008, 0x0000, snacid, &bs);
-
- byte_stream_destroy(&bs);
-}
-
-/* Subtype 0x0009 - Delete Rate Parameter */
-void
-aim_srv_rates_delparam(OscarData *od, FlapConnection *conn)
-{
- ByteStream bs;
- aim_snacid_t snacid;
- GSList *tmp;
-
- byte_stream_new(&bs, 502);
-
- for (tmp = conn->rateclasses; tmp != NULL; tmp = tmp->next)
- {
- struct rateclass *rateclass;
- rateclass = tmp->data;
- byte_stream_put16(&bs, rateclass->classid);
- }
-
- snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0009, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0009, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0008, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -558,40 +535,6 @@ serverpause(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *f
return ret;
}
-/*
- * Subtype 0x000c - Service Pause Acknowledgement
- *
- * It is rather important that aim_srv_sendpauseack() gets called for the exact
- * same connection that the Server Pause callback was called for, since
- * libfaim extracts the data for the SNAC from the connection structure.
- *
- * Of course, if you don't do that, more bad things happen than just what
- * libfaim can cause.
- *
- */
-void
-aim_srv_sendpauseack(OscarData *od, FlapConnection *conn)
-{
- ByteStream bs;
- aim_snacid_t snacid;
- GSList *cur;
-
- byte_stream_new(&bs, 1014);
-
- /*
- * This list should have all the groups that the original
- * Host Online / Server Ready said this host supports. And
- * we want them all back after the migration.
- */
- for (cur = conn->groups; cur != NULL; cur = cur->next)
- byte_stream_put16(&bs, GPOINTER_TO_UINT(cur->data));
-
- snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x000c, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x000c, 0x0000, snacid, &bs);
-
- byte_stream_destroy(&bs);
-}
-
/* Subtype 0x000d - Service Resume */
static int
serverresume(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
@@ -643,7 +586,7 @@ evilnotify(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *fr
newevil = byte_stream_get16(bs);
- if (byte_stream_empty(bs))
+ if (byte_stream_bytes_left(bs))
aim_info_extract(od, bs, &userinfo);
if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
@@ -770,36 +713,6 @@ motd(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, a
}
/*
- * Subtype 0x0014 - Set privacy flags
- *
- * Normally 0x03.
- *
- * Bit 1: Allows other AIM users to see how long you've been idle.
- * Bit 2: Allows other AIM users to see how long you've been a member.
- *
- */
-void
-aim_srv_setprivacyflags(OscarData *od, FlapConnection *conn, guint32 flags)
-{
- aim_genericreq_l(od, conn, SNAC_FAMILY_OSERVICE, 0x0014, &flags);
-}
-
-/*
- * Subtype 0x0016 - No-op
- *
- * WinAIM sends these every 4min or so to keep the connection alive. Its not
- * really necessary.
- *
- * Wha? No? Since when? I think WinAIM sends an empty channel 5
- * FLAP as a no-op...
- */
-void
-aim_srv_nop(OscarData *od, FlapConnection *conn)
-{
- aim_genericreq_n(od, conn, SNAC_FAMILY_OSERVICE, 0x0016);
-}
-
-/*
* Subtype 0x0017 - Set client versions
*
* If you've seen the clientonline/clientready SNAC you're probably
@@ -837,7 +750,7 @@ aim_srv_setversions(OscarData *od, FlapConnection *conn)
}
snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0017, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0017, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0017, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -850,8 +763,8 @@ hostversions(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *
guint8 *versions;
/* This is frivolous. (Thank you SmarterChild.) */
- vercount = byte_stream_empty(bs)/4;
- versions = byte_stream_getraw(bs, byte_stream_empty(bs));
+ vercount = byte_stream_bytes_left(bs)/4;
+ versions = byte_stream_getraw(bs, byte_stream_bytes_left(bs));
g_free(versions);
/*
@@ -899,16 +812,6 @@ aim_srv_setextrainfo(OscarData *od,
AIM_ICQ_STATE_HIDEIP | AIM_ICQ_STATE_DIRECTREQUIREAUTH);
}
-#if 0
- if (other_stuff_that_isnt_implemented)
- {
- aim_tlvlist_add_raw(&tlvlist, 0x000c, 0x0025,
- chunk_of_x25_bytes_with_ip_address_etc);
- aim_tlvlist_add_raw(&tlvlist, 0x0011, 0x0005, unknown 0x01 61 10 f6 41);
- aim_tlvlist_add_16(&tlvlist, 0x0012, unknown 0x00 00);
- }
-#endif
-
if (setstatusmsg)
{
size_t statusmsglen, itmsurllen;
@@ -932,13 +835,57 @@ aim_srv_setextrainfo(OscarData *od,
aim_tlvlist_free(tlvlist);
snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x001e, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x001e, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x001e, snacid, &bs);
byte_stream_destroy(&bs);
return 0;
}
+/* Send dummy DC (direct connect) information to the server.
+ * Direct connect is ICQ's counterpart for AIM's DirectIM,
+ * as far as I can tell. Anyway, we don't support it;
+ * the reason to send this packet is that some clients
+ * (Miranda, QIP) won't send us channel 2 ICBM messages
+ * unless we specify DC version >= 8.
+ *
+ * See #12044 for more information.
+ */
+void
+aim_srv_set_dc_info(OscarData *od)
+{
+ ByteStream bs, tlv0c;
+ aim_snacid_t snacid;
+ GSList *tlvlist = NULL;
+
+ /* http://iserverd.khstu.ru/oscar/snac_01_1e.html has a nice analysis of what goes in 0xc tlv.
+ * Kopete sends a dummy DC info, too, so I just copied the values from them.
+ */
+ byte_stream_new(&tlv0c, 4*2 + 1 + 2 + 4*6 + 2);
+ byte_stream_put32(&tlv0c, 0x0);
+ byte_stream_put32(&tlv0c, 0x0);
+ byte_stream_put8(&tlv0c, 0x0); /* We don't support DC */
+ byte_stream_put16(&tlv0c, 8); /* DC version */
+ byte_stream_put32(&tlv0c, 0x0);
+ byte_stream_put32(&tlv0c, 0x50);
+ byte_stream_put32(&tlv0c, 0x3);
+ byte_stream_put32(&tlv0c, 0x0);
+ byte_stream_put32(&tlv0c, 0x0);
+ byte_stream_put32(&tlv0c, 0x0);
+ byte_stream_put16(&tlv0c, 0x0);
+ aim_tlvlist_add_raw(&tlvlist, 0x000c, byte_stream_curpos(&tlv0c), tlv0c.data);
+ byte_stream_destroy(&tlv0c);
+
+ byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
+ aim_tlvlist_write(&bs, &tlvlist);
+ aim_tlvlist_free(tlvlist);
+
+ snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x001e, 0x0000, NULL, 0);
+ flap_connection_send_snac(od, flap_connection_findbygroup(od, SNAC_FAMILY_ICBM), SNAC_FAMILY_OSERVICE, 0x001e, snacid, &bs);
+
+ 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.
@@ -1077,7 +1024,7 @@ aim_sendmemblock(OscarData *od, FlapConnection *conn, guint32 offset, guint32 le
}
snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0020, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0020, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0020, snacid, &bs);
byte_stream_destroy(&bs);
@@ -1094,8 +1041,6 @@ aim_sendmemblock(OscarData *od, FlapConnection *conn, guint32 offset, guint32 le
static int
aim_parse_extstatus(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
{
- int ret = 0;
- aim_rxcallback_t userfunc;
guint16 type;
guint8 flags, length;
@@ -1108,25 +1053,54 @@ aim_parse_extstatus(OscarData *od, FlapConnection *conn, aim_module_t *mod, Flap
* A flag of 0x40 could mean "I don't have your icon, upload it"
*/
- if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) {
- switch (type) {
+ switch (type) {
case 0x0000:
case 0x0001: { /* buddy icon checksum */
/* not sure what the difference between 1 and 0 is */
guint8 *md5 = byte_stream_getraw(bs, length);
- ret = userfunc(od, conn, frame, type, flags, length, md5);
+
+ if ((flags == 0x00) || (flags == 0x41)) {
+ if (!flap_connection_getbytype(od, SNAC_FAMILY_BART) && !od->iconconnecting) {
+ od->iconconnecting = TRUE;
+ od->set_icon = TRUE;
+ 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);
+ 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);
+ }
+ }
+ } else if (flags == 0x81) {
+ PurpleAccount *account = purple_connection_get_account(od->gc);
+ PurpleStoredImage *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_free(md5);
- } break;
- case 0x0002: { /* available message */
+ } break;
+
+ case 0x0002: {
+ /* We just set an available message? */
/* there is a second length that is just for the message */
char *msg = byte_stream_getstr(bs, byte_stream_get16(bs));
- ret = userfunc(od, conn, frame, msg);
g_free(msg);
- } break;
- }
+ } break;
}
- return ret;
+ return 0;
}
static int
diff --git a/libpurple/protocols/oscar/family_userlookup.c b/libpurple/protocols/oscar/family_userlookup.c
index 9f766c142d..056e43d4e4 100644
--- a/libpurple/protocols/oscar/family_userlookup.c
+++ b/libpurple/protocols/oscar/family_userlookup.c
@@ -75,7 +75,7 @@ int aim_search_address(OscarData *od, const char *address)
byte_stream_putstr(&bs, address);
snacid = aim_cachesnac(od, SNAC_FAMILY_USERLOOKUP, 0x0002, 0x0000, address, strlen(address)+1);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_USERLOOKUP, 0x0002, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_USERLOOKUP, 0x0002, snacid, &bs);
byte_stream_destroy(&bs);
diff --git a/libpurple/protocols/oscar/flap_connection.c b/libpurple/protocols/oscar/flap_connection.c
index 3255e0f37a..8b4dc621d4 100644
--- a/libpurple/protocols/oscar/flap_connection.c
+++ b/libpurple/protocols/oscar/flap_connection.c
@@ -212,7 +212,7 @@ static gboolean flap_connection_send_queued(gpointer data)
* only if all high priority SNACs have been sent.
*/
void
-flap_connection_send_snac_with_priority(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, guint16 flags, aim_snacid_t snacid, ByteStream *data, gboolean high_priority)
+flap_connection_send_snac_with_priority(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, aim_snacid_t snacid, ByteStream *data, gboolean high_priority)
{
FlapFrame *frame;
guint32 length;
@@ -222,7 +222,7 @@ flap_connection_send_snac_with_priority(OscarData *od, FlapConnection *conn, gui
length = data != NULL ? data->offset : 0;
frame = flap_frame_new(od, 0x02, 10 + length);
- aim_putsnac(&frame->data, family, subtype, flags, snacid);
+ aim_putsnac(&frame->data, family, subtype, snacid);
if (length > 0)
{
@@ -284,9 +284,9 @@ flap_connection_send_snac_with_priority(OscarData *od, FlapConnection *conn, gui
}
void
-flap_connection_send_snac(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, guint16 flags, aim_snacid_t snacid, ByteStream *data)
+flap_connection_send_snac(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, aim_snacid_t snacid, ByteStream *data)
{
- flap_connection_send_snac_with_priority(od, conn, family, subtype, flags, snacid, data, TRUE);
+ flap_connection_send_snac_with_priority(od, conn, family, subtype, snacid, data, TRUE);
}
/**
@@ -733,7 +733,7 @@ parse_snac(OscarData *od, FlapConnection *conn, FlapFrame *frame)
aim_module_t *cur;
aim_modsnac_t snac;
- if (byte_stream_empty(&frame->data) < 10)
+ if (byte_stream_bytes_left(&frame->data) < 10)
return;
snac.family = byte_stream_get16(&frame->data);
@@ -800,7 +800,7 @@ parse_flap_ch4(OscarData *od, FlapConnection *conn, FlapFrame *frame)
GSList *tlvlist;
char *msg = NULL;
- if (byte_stream_empty(&frame->data) == 0) {
+ if (byte_stream_bytes_left(&frame->data) == 0) {
/* XXX should do something with this */
return;
}
@@ -931,18 +931,6 @@ flap_connection_recv(FlapConnection *conn)
break;
}
- /* Verify the sequence number sent by the server. */
-#if 0
- /* TODO: Need to initialize conn->seqnum_in somewhere before we can use this. */
- if (aimutil_get16(&conn->header[1]) != conn->seqnum_in++)
- {
- /* Received an out-of-order FLAP! */
- flap_connection_schedule_destroy(conn,
- OSCAR_DISCONNECT_INVALID_DATA, NULL);
- break;
- }
-#endif
-
/* Initialize a new temporary FlapFrame for incoming data */
conn->buffer_incoming.channel = aimutil_get8(&conn->header[1]);
conn->buffer_incoming.seqnum = aimutil_get16(&conn->header[2]);
@@ -1074,8 +1062,8 @@ flap_connection_send_byte_stream(ByteStream *bs, FlapConnection *conn, size_t co
return;
/* Make sure we don't send past the end of the bs */
- if (count > byte_stream_empty(bs))
- count = byte_stream_empty(bs); /* truncate to remaining space */
+ if (count > byte_stream_bytes_left(bs))
+ count = byte_stream_bytes_left(bs); /* truncate to remaining space */
if (count == 0)
return;
diff --git a/libpurple/protocols/oscar/libaim.c b/libpurple/protocols/oscar/libaim.c
index 5345b25d63..3862930728 100644
--- a/libpurple/protocols/oscar/libaim.c
+++ b/libpurple/protocols/oscar/libaim.c
@@ -20,11 +20,12 @@
*
*/
-/* libaim is the AIM protocol plugin. It is linked against liboscarcommon,
+/* libaim is the AIM protocol plugin. It is linked against liboscar,
* which contains all the shared implementation code with libicq
*/
#include "oscarcommon.h"
+#include "oscar.h"
static PurplePluginProtocolInfo prpl_info =
{
@@ -57,7 +58,7 @@ static PurplePluginProtocolInfo prpl_info =
oscar_add_deny, /* add_deny */
oscar_rem_permit, /* rem_permit */
oscar_rem_deny, /* rem_deny */
- oscar_set_permit_deny, /* set_permit_deny */
+ oscar_set_aim_permdeny, /* set_permit_deny */
oscar_join_chat, /* join_chat */
NULL, /* reject_chat */
oscar_get_chat_name, /* get_chat_name */
@@ -97,7 +98,9 @@ static PurplePluginProtocolInfo prpl_info =
NULL, /* get_account_text_table */
NULL, /* initiate_media */
NULL, /* get_media_caps */
- NULL /* get_moods */
+ NULL, /* get_moods */
+ NULL, /* set_public_alias */
+ NULL /* get_public_alias */
};
static PurplePluginInfo info =
diff --git a/libpurple/protocols/oscar/libicq.c b/libpurple/protocols/oscar/libicq.c
index 2972466075..f7bfecaa79 100644
--- a/libpurple/protocols/oscar/libicq.c
+++ b/libpurple/protocols/oscar/libicq.c
@@ -20,7 +20,7 @@
*
*/
-/* libicq is the ICQ protocol plugin. It is linked against liboscarcommon,
+/* libicq is the ICQ protocol plugin. It is linked against liboscar,
* which contains all the shared implementation code with libaim
*/
@@ -63,11 +63,11 @@ static PurplePluginProtocolInfo prpl_info =
NULL, /* add_buddies */
oscar_remove_buddy, /* remove_buddy */
NULL, /* remove_buddies */
- oscar_add_permit, /* add_permit */
+ NULL, /* add_permit */
oscar_add_deny, /* add_deny */
- oscar_rem_permit, /* rem_permit */
+ NULL, /* rem_permit */
oscar_rem_deny, /* rem_deny */
- oscar_set_permit_deny, /* set_permit_deny */
+ NULL, /* set_permit_deny */
oscar_join_chat, /* join_chat */
NULL, /* reject_chat */
oscar_get_chat_name, /* get_chat_name */
@@ -109,6 +109,8 @@ static PurplePluginProtocolInfo prpl_info =
NULL, /* initiate_media */
NULL, /* can_do_media */
oscar_get_purple_moods, /* get_moods */
+ NULL, /* set_public_alias */
+ NULL /* get_public_alias */
};
static PurplePluginInfo info =
diff --git a/libpurple/protocols/oscar/misc.c b/libpurple/protocols/oscar/misc.c
index 4ddc562641..ed6b569914 100644
--- a/libpurple/protocols/oscar/misc.c
+++ b/libpurple/protocols/oscar/misc.c
@@ -41,7 +41,7 @@ aim_genericreq_n(OscarData *od, FlapConnection *conn, guint16 family, guint16 su
{
aim_snacid_t snacid = 0x00000000;
- flap_connection_send_snac(od, conn, family, subtype, 0x0000, snacid, NULL);
+ flap_connection_send_snac(od, conn, family, subtype, snacid, NULL);
}
void
@@ -51,7 +51,7 @@ aim_genericreq_n_snacid(OscarData *od, FlapConnection *conn, guint16 family, gui
snacid = aim_cachesnac(od, family, subtype, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, family, subtype, 0x0000, snacid, NULL);
+ flap_connection_send_snac(od, conn, family, subtype, snacid, NULL);
}
void
@@ -72,30 +72,7 @@ aim_genericreq_l(OscarData *od, FlapConnection *conn, guint16 family, guint16 su
byte_stream_put32(&bs, *longdata);
- flap_connection_send_snac(od, conn, family, subtype, 0x0000, snacid, &bs);
-
- byte_stream_destroy(&bs);
-}
-
-void
-aim_genericreq_s(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype, guint16 *shortdata)
-{
- ByteStream bs;
- aim_snacid_t snacid;
-
- if (!shortdata)
- {
- aim_genericreq_n(od, conn, family, subtype);
- return;
- }
-
- byte_stream_new(&bs, 2);
-
- snacid = aim_cachesnac(od, family, subtype, 0x0000, NULL, 0);
-
- byte_stream_put16(&bs, *shortdata);
-
- flap_connection_send_snac(od, conn, family, subtype, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, family, subtype, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -114,15 +91,16 @@ generror(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *fram
snac2 = aim_remsnac(od, snac->id);
- if (byte_stream_empty(bs))
+ if (byte_stream_bytes_left(bs))
error = byte_stream_get16(bs);
if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
ret = userfunc(od, conn, frame, error, snac2 ? snac2->data : NULL);
- if (snac2)
+ if (snac2) {
g_free(snac2->data);
- g_free(snac2);
+ g_free(snac2);
+ }
return ret;
}
diff --git a/libpurple/protocols/oscar/msgcookie.c b/libpurple/protocols/oscar/msgcookie.c
index d344b0991c..19a3e43aea 100644
--- a/libpurple/protocols/oscar/msgcookie.c
+++ b/libpurple/protocols/oscar/msgcookie.c
@@ -177,18 +177,3 @@ int aim_cookie_free(OscarData *od, IcbmCookie *cookie)
return 0;
}
-
-/* XXX I hate switch */
-int aim_msgcookie_gettype(guint64 type)
-{
- /* XXX: hokey-assed. needs fixed. */
- switch(type) {
- case OSCAR_CAPABILITY_BUDDYICON: return AIM_COOKIETYPE_OFTICON;
- case OSCAR_CAPABILITY_TALK: return AIM_COOKIETYPE_OFTVOICE;
- case OSCAR_CAPABILITY_DIRECTIM: return AIM_COOKIETYPE_OFTIMAGE;
- case OSCAR_CAPABILITY_CHAT: return AIM_COOKIETYPE_CHAT;
- case OSCAR_CAPABILITY_GETFILE: return AIM_COOKIETYPE_OFTGET;
- case OSCAR_CAPABILITY_SENDFILE: return AIM_COOKIETYPE_OFTSEND;
- default: return AIM_COOKIETYPE_UNKNOWN;
- }
-}
diff --git a/libpurple/protocols/oscar/odc.c b/libpurple/protocols/oscar/odc.c
index eb741b3cb2..e2a7745128 100644
--- a/libpurple/protocols/oscar/odc.c
+++ b/libpurple/protocols/oscar/odc.c
@@ -19,6 +19,7 @@
*/
/* From the oscar PRPL */
+#include "encoding.h"
#include "oscar.h"
#include "peer.h"
@@ -89,7 +90,7 @@ peer_odc_send(PeerConnection *conn, OdcFrame *frame)
ByteStream bs;
purple_debug_info("oscar", "Outgoing ODC frame to %s with "
- "type=0x%04x, flags=0x%04x, payload length=%u\n",
+ "type=0x%04x, flags=0x%04x, payload length=%" G_GSIZE_FORMAT "\n",
conn->bn, frame->type, frame->flags, frame->payload.len);
account = purple_connection_get_account(conn->od->gc);
@@ -366,8 +367,7 @@ peer_odc_handle_payload(PeerConnection *conn, const char *msg, size_t len, int e
g_datalist_clear(&attributes);
/* Append the message up to the tag */
- utf8 = purple_plugin_oscar_decode_im_part(account, conn->bn,
- encoding, 0x0000, tmp, start - tmp);
+ utf8 = oscar_decode_im(account, conn->bn, encoding, tmp, start - tmp);
if (utf8 != NULL) {
g_string_append(newmsg, utf8);
g_free(utf8);
@@ -386,8 +386,7 @@ peer_odc_handle_payload(PeerConnection *conn, const char *msg, size_t len, int e
/* Append any remaining message data */
if (tmp <= msgend)
{
- utf8 = purple_plugin_oscar_decode_im_part(account, conn->bn,
- encoding, 0x0000, tmp, msgend - tmp);
+ utf8 = oscar_decode_im(account, conn->bn, encoding, tmp, msgend - tmp);
if (utf8 != NULL) {
g_string_append(newmsg, utf8);
g_free(utf8);
@@ -506,7 +505,7 @@ peer_odc_recv_frame(PeerConnection *conn, ByteStream *bs)
byte_stream_getrawbuf(bs, frame->bn, 32);
purple_debug_info("oscar", "Incoming ODC frame from %s with "
- "type=0x%04x, flags=0x%04x, payload length=%u\n",
+ "type=0x%04x, flags=0x%04x, payload length=%" G_GSIZE_FORMAT "\n",
frame->bn, frame->type, frame->flags, frame->payload.len);
if (!conn->ready)
diff --git a/libpurple/protocols/oscar/oft.c b/libpurple/protocols/oscar/oft.c
index 47891cebca..aa2d84b380 100644
--- a/libpurple/protocols/oscar/oft.c
+++ b/libpurple/protocols/oscar/oft.c
@@ -240,7 +240,7 @@ void
peer_oft_close(PeerConnection *conn)
{
/*
- * If canceled by local user, and we're receiving a file, and
+ * If cancelled by local user, and we're receiving a file, and
* we're not connected/ready then send an ICBM cancel message.
*/
if ((purple_xfer_get_status(conn->xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) &&
diff --git a/libpurple/protocols/oscar/oscar.c b/libpurple/protocols/oscar/oscar.c
index 078de9055a..554877e2c5 100644
--- a/libpurple/protocols/oscar/oscar.c
+++ b/libpurple/protocols/oscar/oscar.c
@@ -37,6 +37,7 @@
#include "conversation.h"
#include "core.h"
#include "debug.h"
+#include "encoding.h"
#include "imgstore.h"
#include "network.h"
#include "notify.h"
@@ -46,27 +47,12 @@
#include "request.h"
#include "util.h"
#include "version.h"
+#include "visibility.h"
#include "oscarcommon.h"
#include "oscar.h"
#include "peer.h"
-#define OSCAR_STATUS_ID_INVISIBLE "invisible"
-#define OSCAR_STATUS_ID_OFFLINE "offline"
-#define OSCAR_STATUS_ID_AVAILABLE "available"
-#define OSCAR_STATUS_ID_AWAY "away"
-#define OSCAR_STATUS_ID_DND "dnd"
-#define OSCAR_STATUS_ID_NA "na"
-#define OSCAR_STATUS_ID_OCCUPIED "occupied"
-#define OSCAR_STATUS_ID_FREE4CHAT "free4chat"
-#define OSCAR_STATUS_ID_CUSTOM "custom"
-#define OSCAR_STATUS_ID_MOBILE "mobile"
-#define OSCAR_STATUS_ID_EVIL "evil"
-#define OSCAR_STATUS_ID_DEPRESSION "depression"
-#define OSCAR_STATUS_ID_ATHOME "athome"
-#define OSCAR_STATUS_ID_ATWORK "atwork"
-#define OSCAR_STATUS_ID_LUNCH "lunch"
-
#define AIMHASHDATA "http://pidgin.im/aim_data.php3"
#define OSCAR_CONNECT_STEPS 6
@@ -82,11 +68,11 @@ static guint64 purple_caps =
| OSCAR_CAPABILITY_TYPING
| OSCAR_CAPABILITY_ICQSERVERRELAY
| OSCAR_CAPABILITY_NEWCAPS
- | OSCAR_CAPABILITY_XTRAZ;
+ | OSCAR_CAPABILITY_XTRAZ
+ | OSCAR_CAPABILITY_HTML_MSGS;
static guint8 features_aim[] = {0x01, 0x01, 0x01, 0x02};
-static guint8 features_icq[] = {0x01, 0x06};
-static guint8 features_icq_offline[] = {0x01};
+static guint8 features_icq[] = {0x01};
static guint8 ck[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
struct create_room {
@@ -100,84 +86,6 @@ struct oscar_ask_directim_data
char *who;
};
-/*
- * Various PRPL-specific buddy info that we want to keep track of
- * Some other info is maintained by locate.c, and I'd like to move
- * the rest of this to libfaim, mostly im.c
- *
- * TODO: More of this should use the status API.
- */
-struct buddyinfo {
- gboolean typingnot;
- guint32 ipaddr;
-
- unsigned long ico_me_len;
- unsigned long ico_me_csum;
- time_t ico_me_time;
- gboolean ico_informed;
-
- unsigned long ico_len;
- unsigned long ico_csum;
- time_t ico_time;
- gboolean ico_need;
- gboolean ico_sent;
-};
-
-struct name_data {
- PurpleConnection *gc;
- gchar *name;
- gchar *nick;
-};
-
-static const char * const msgerrreason[] = {
- N_("Invalid error"),
- N_("Invalid SNAC"),
- N_("Rate to host"),
- N_("Rate to client"),
- N_("Not logged in"),
- N_("Service unavailable"),
- N_("Service not defined"),
- N_("Obsolete SNAC"),
- N_("Not supported by host"),
- N_("Not supported by client"),
- N_("Refused by client"),
- N_("Reply too big"),
- N_("Responses lost"),
- N_("Request denied"),
- N_("Busted SNAC payload"),
- N_("Insufficient rights"),
- N_("In local permit/deny"),
- N_("Warning level too high (sender)"),
- N_("Warning level too high (receiver)"),
- N_("User temporarily unavailable"),
- N_("No match"),
- N_("List overflow"),
- N_("Request ambiguous"),
- N_("Queue full"),
- N_("Not while on AOL")
-};
-static const int msgerrreasonlen = G_N_ELEMENTS(msgerrreason);
-
-static const char * const errcodereason[] = {
- N_("Invalid error"),
- N_("Not logged in"),
- N_("Cannot receive IM due to parental controls"),
- N_("Cannot send SMS without accepting terms"),
- N_("Cannot send SMS"), /* SMS_WITHOUT_DISCLAIMER is weird */
- N_("Cannot send SMS to this country"),
- N_("Unknown error"), /* Undocumented */
- N_("Unknown error"), /* Undocumented */
- N_("Cannot send SMS to unknown country"),
- N_("Bot accounts cannot initiate IMs"),
- N_("Bot account cannot IM this user"),
- N_("Bot account reached IM limit"),
- N_("Bot account reached daily IM limit"),
- N_("Bot account reached monthly IM limit"),
- N_("Unable to receive offline messages"),
- N_("Offline message store full")
-};
-static const int errcodereasonlen = G_N_ELEMENTS(errcodereason);
-
/* All the libfaim->purple callback functions */
/* Only used when connecting with the old-style BUCP login */
@@ -193,7 +101,6 @@ static int purple_parse_offgoing (OscarData *, FlapConnection *, FlapFrame *,
static int purple_parse_incoming_im(OscarData *, FlapConnection *, FlapFrame *, ...);
static int purple_parse_misses (OscarData *, FlapConnection *, FlapFrame *, ...);
static int purple_parse_clientauto (OscarData *, FlapConnection *, FlapFrame *, ...);
-static int purple_parse_userinfo (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 *, ...);
@@ -202,27 +109,16 @@ static int purple_conv_chat_info_update (OscarData *, FlapConnection *, FlapFram
static int purple_conv_chat_incoming_msg(OscarData *, FlapConnection *, FlapFrame *, ...);
static int purple_email_parseupdate(OscarData *, FlapConnection *, FlapFrame *, ...);
static int purple_icon_parseicon (OscarData *, FlapConnection *, FlapFrame *, ...);
-static int oscar_icon_req (OscarData *, FlapConnection *, FlapFrame *, ...);
-static int purple_parse_msgack (OscarData *, FlapConnection *, FlapFrame *, ...);
-static int purple_parse_evilnotify (OscarData *, FlapConnection *, FlapFrame *, ...);
static int purple_parse_searcherror(OscarData *, FlapConnection *, FlapFrame *, ...);
static int purple_parse_searchreply(OscarData *, FlapConnection *, FlapFrame *, ...);
static int purple_bosrights (OscarData *, FlapConnection *, FlapFrame *, ...);
static int purple_connerr (OscarData *, FlapConnection *, FlapFrame *, ...);
-static int purple_parse_msgerr (OscarData *, FlapConnection *, FlapFrame *, ...);
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_locerr (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 *, ...);
-#ifdef OLDSTYLE_ICQ_OFFLINEMSGS
-static int purple_offlinemsg (OscarData *, FlapConnection *, FlapFrame *, ...);
-static int purple_offlinemsgdone (OscarData *, FlapConnection *, FlapFrame *, ...);
-#endif /* OLDSTYLE_ICQ_OFFLINEMSGS */
-static int purple_icqalias (OscarData *, FlapConnection *, FlapFrame *, ...);
-static int purple_icqinfo (OscarData *, FlapConnection *, FlapFrame *, ...);
static int purple_popup (OscarData *, FlapConnection *, FlapFrame *, ...);
static int purple_ssi_parseerr (OscarData *, FlapConnection *, FlapFrame *, ...);
static int purple_ssi_parserights (OscarData *, FlapConnection *, FlapFrame *, ...);
@@ -238,10 +134,10 @@ static void purple_icons_fetch(PurpleConnection *gc);
void oscar_set_info(PurpleConnection *gc, const char *info);
static void oscar_set_info_and_status(PurpleAccount *account, gboolean setinfo, const char *rawinfo, gboolean setstatus, PurpleStatus *status);
-static void oscar_set_extendedstatus(PurpleConnection *gc);
+static void oscar_set_extended_status(PurpleConnection *gc);
static gboolean purple_ssi_rerequestdata(gpointer data);
-static void oscar_free_name_data(struct name_data *data) {
+void oscar_free_name_data(struct name_data *data) {
g_free(data->name);
g_free(data->nick);
g_free(data);
@@ -256,536 +152,6 @@ const char *oscar_get_locale_charset(void) {
}
#endif
-/**
- * Determine how we can send this message. Per the warnings elsewhere
- * in this file, these little checks determine the simplest encoding
- * we can use for a given message send using it.
- */
-static guint32
-oscar_charset_check(const char *utf8)
-{
- int i = 0;
- int charset = AIM_CHARSET_ASCII;
-
- /*
- * Can we get away with using our custom encoding?
- */
- while (utf8[i])
- {
- if ((unsigned char)utf8[i] > 0x7f) {
- /* not ASCII! */
- charset = AIM_CHARSET_LATIN_1;
- break;
- }
- i++;
- }
-
- /*
- * Must we send this message as UNICODE (in the UTF-16BE encoding)?
- */
- while (utf8[i])
- {
- /* ISO-8859-1 is 0x00-0xbf in the first byte
- * followed by 0xc0-0xc3 in the second */
- if ((unsigned char)utf8[i] < 0x80) {
- i++;
- continue;
- } else if (((unsigned char)utf8[i] & 0xfc) == 0xc0 &&
- ((unsigned char)utf8[i + 1] & 0xc0) == 0x80) {
- i += 2;
- continue;
- }
- charset = AIM_CHARSET_UNICODE;
- break;
- }
-
- return charset;
-}
-
-/**
- * Take a string of the form charset="bleh" where bleh is
- * one of us-ascii, utf-8, iso-8859-1, or unicode-2-0, and
- * return a newly allocated string containing bleh.
- */
-gchar *
-oscar_encoding_extract(const char *encoding)
-{
- gchar *ret = NULL;
- char *begin, *end;
-
- g_return_val_if_fail(encoding != NULL, NULL);
-
- /* Make sure encoding begins with charset= */
- if (strncmp(encoding, "text/aolrtf; charset=", 21) &&
- strncmp(encoding, "text/x-aolrtf; charset=", 23) &&
- strncmp(encoding, "text/plain; charset=", 20))
- {
- return NULL;
- }
-
- begin = strchr(encoding, '"');
- end = strrchr(encoding, '"');
-
- if ((begin == NULL) || (end == NULL) || (begin >= end))
- return NULL;
-
- ret = g_strndup(begin+1, (end-1) - begin);
-
- return ret;
-}
-
-gchar *
-oscar_encoding_to_utf8(PurpleAccount *account, const char *encoding, const char *text, int textlen)
-{
- gchar *utf8 = NULL;
-
- if ((encoding == NULL) || encoding[0] == '\0') {
- purple_debug_info("oscar", "Empty encoding, assuming UTF-8\n");
- } else if (!g_ascii_strcasecmp(encoding, "iso-8859-1")) {
- utf8 = g_convert(text, textlen, "UTF-8", "iso-8859-1", NULL, NULL, NULL);
- } else if (!g_ascii_strcasecmp(encoding, "ISO-8859-1-Windows-3.1-Latin-1") ||
- !g_ascii_strcasecmp(encoding, "us-ascii"))
- {
- utf8 = g_convert(text, textlen, "UTF-8", "Windows-1252", NULL, NULL, NULL);
- } else if (!g_ascii_strcasecmp(encoding, "unicode-2-0")) {
- /* Some official ICQ clients are apparently total crack,
- * and have been known to save a UTF-8 string converted
- * from the locale character set to UTF-16 (not from UTF-8
- * to UTF-16!) in the away message. This hack should find
- * and do something (un)reasonable with that, and not
- * mess up too much else. */
- const gchar *charset = purple_account_get_string(account, "encoding", NULL);
- if (charset) {
- gsize len;
- utf8 = g_convert(text, textlen, charset, "UTF-16BE", &len, NULL, NULL);
- if (!utf8 || len != textlen || !g_utf8_validate(utf8, -1, NULL)) {
- g_free(utf8);
- utf8 = NULL;
- } else {
- purple_debug_info("oscar", "Used broken ICQ fallback encoding\n");
- }
- }
- if (!utf8)
- utf8 = g_convert(text, textlen, "UTF-8", "UTF-16BE", NULL, NULL, NULL);
- } else if (g_ascii_strcasecmp(encoding, "utf-8")) {
- purple_debug_warning("oscar", "Unrecognized character encoding \"%s\", "
- "attempting to convert to UTF-8 anyway\n", encoding);
- utf8 = g_convert(text, textlen, "UTF-8", encoding, NULL, NULL, NULL);
- }
-
- /*
- * If utf8 is still NULL then either the encoding is utf-8 or
- * we have been unable to convert the text to utf-8 from the encoding
- * that was specified. So we check if the text is valid utf-8 then
- * just copy it.
- */
- if (utf8 == NULL) {
- if (textlen != 0 && *text != '\0'
- && !g_utf8_validate(text, textlen, NULL))
- utf8 = g_strdup(_("(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.)"));
- else
- utf8 = g_strndup(text, textlen);
- }
-
- return utf8;
-}
-
-static gchar *
-oscar_utf8_try_convert(PurpleAccount *account, OscarData *od, const gchar *msg)
-{
- const char *charset = NULL;
- char *ret = NULL;
-
- if (od->icq)
- charset = purple_account_get_string(account, "encoding", NULL);
-
- if(charset && *charset)
- ret = g_convert(msg, -1, "UTF-8", charset, NULL, NULL, NULL);
-
- if(!ret)
- ret = purple_utf8_try_convert(msg);
-
- return ret;
-}
-
-static gchar *
-purple_plugin_oscar_convert_to_utf8(const gchar *data, gsize datalen, const char *charsetstr, gboolean fallback)
-{
- gchar *ret = NULL;
- GError *err = NULL;
-
- if ((charsetstr == NULL) || (*charsetstr == '\0'))
- return NULL;
-
- if (g_ascii_strcasecmp("UTF-8", charsetstr)) {
- if (fallback)
- ret = g_convert_with_fallback(data, datalen, "UTF-8", charsetstr, "?", NULL, NULL, &err);
- else
- ret = g_convert(data, datalen, "UTF-8", charsetstr, NULL, NULL, &err);
- if (err != NULL) {
- purple_debug_warning("oscar", "Conversion from %s failed: %s.\n",
- charsetstr, err->message);
- g_error_free(err);
- }
- } else {
- if (g_utf8_validate(data, datalen, NULL))
- ret = g_strndup(data, datalen);
- else
- purple_debug_warning("oscar", "String is not valid UTF-8.\n");
- }
-
- return ret;
-}
-
-/**
- * This attemps to decode an incoming IM into a UTF8 string.
- *
- * We try decoding using two different character sets. The charset
- * specified in the IM determines the order in which we attempt to
- * decode. We do this because there are lots of broken ICQ clients
- * that don't correctly send non-ASCII messages. And if Purple isn't
- * able to deal with that crap, then people complain like banshees.
- * charsetstr1 is always set to what the correct encoding should be.
- */
-gchar *
-purple_plugin_oscar_decode_im_part(PurpleAccount *account, const char *sourcebn, guint16 charset, guint16 charsubset, const gchar *data, gsize datalen)
-{
- gchar *ret = NULL;
- const gchar *charsetstr1, *charsetstr2, *charsetstr3 = NULL;
-
- if ((datalen == 0) || (data == NULL))
- return NULL;
-
- if (charset == AIM_CHARSET_UNICODE) {
- charsetstr1 = "UTF-16BE";
- charsetstr2 = "UTF-8";
- } else if (charset == AIM_CHARSET_LATIN_1) {
- if ((sourcebn != NULL) && oscar_util_valid_name_icq(sourcebn))
- charsetstr1 = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
- else
- charsetstr1 = "ISO-8859-1";
- charsetstr2 = "UTF-8";
- } else if (charset == AIM_CHARSET_ASCII) {
- /* Should just be "ASCII" */
- charsetstr1 = "ASCII";
- charsetstr2 = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
- } else if (charset == 0x000d) {
- /* iChat sending unicode over a Direct IM connection = UTF-8 */
- /* Mobile AIM client on multiple devices (including Blackberry Tour, Nokia 3100, and LG VX6000) = ISO-8859-1 */
- charsetstr1 = "UTF-8";
- charsetstr2 = "ISO-8859-1";
- charsetstr3 = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
- } else {
- /* Unknown, hope for valid UTF-8... */
- charsetstr1 = "UTF-8";
- charsetstr2 = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
- }
-
- purple_debug_info("oscar", "Parsing IM part, charset=0x%04hx, charsubset=0x%04hx, datalen=%" G_GSIZE_FORMAT ", choice1=%s, choice2=%s, choice3=%s\n",
- charset, charsubset, datalen, charsetstr1, charsetstr2, (charsetstr3 ? charsetstr3 : ""));
-
- ret = purple_plugin_oscar_convert_to_utf8(data, datalen, charsetstr1, FALSE);
- if (ret == NULL) {
- if (charsetstr3 != NULL) {
- /* Try charsetstr2 without allowing substitutions, then fall through to charsetstr3 if needed */
- ret = purple_plugin_oscar_convert_to_utf8(data, datalen, charsetstr2, FALSE);
- if (ret == NULL)
- ret = purple_plugin_oscar_convert_to_utf8(data, datalen, charsetstr3, TRUE);
- } else {
- /* Try charsetstr2, allowing substitutions */
- ret = purple_plugin_oscar_convert_to_utf8(data, datalen, charsetstr2, TRUE);
- }
- }
- if (ret == NULL) {
- char *str, *salvage, *tmp;
-
- str = g_malloc(datalen + 1);
- strncpy(str, data, datalen);
- str[datalen] = '\0';
- salvage = purple_utf8_salvage(str);
- tmp = g_strdup_printf(_("(There was an error receiving this message. Either you and %s have different encodings selected, or %s has a buggy client.)"),
- sourcebn, sourcebn);
- ret = g_strdup_printf("%s %s", salvage, tmp);
- g_free(tmp);
- g_free(str);
- g_free(salvage);
- }
-
- return ret;
-}
-
-/**
- * Figure out what encoding to use when sending a given outgoing message.
- */
-static void
-purple_plugin_oscar_convert_to_best_encoding(PurpleConnection *gc,
- const char *destbn, const gchar *from,
- gchar **msg, int *msglen_int,
- guint16 *charset, guint16 *charsubset)
-{
- OscarData *od = purple_connection_get_protocol_data(gc);
- PurpleAccount *account = purple_connection_get_account(gc);
- GError *err = NULL;
- aim_userinfo_t *userinfo = NULL;
- const gchar *charsetstr;
- gsize msglen;
-
- /* Attempt to send as ASCII */
- if (oscar_charset_check(from) == AIM_CHARSET_ASCII) {
- *msg = g_convert(from, -1, "ASCII", "UTF-8", NULL, &msglen, NULL);
- *charset = AIM_CHARSET_ASCII;
- *charsubset = 0x0000;
- *msglen_int = msglen;
- return;
- }
-
- /*
- * If we're sending to an ICQ user, and they are in our
- * buddy list, and they are advertising the Unicode
- * capability, and they are online, then attempt to send
- * as UTF-16BE.
- */
- if ((destbn != NULL) && oscar_util_valid_name_icq(destbn))
- userinfo = aim_locate_finduserinfo(od, destbn);
-
- if ((userinfo != NULL) && (userinfo->capabilities & OSCAR_CAPABILITY_UNICODE))
- {
- PurpleBuddy *b;
- b = purple_find_buddy(account, destbn);
- if ((b != NULL) && (PURPLE_BUDDY_IS_ONLINE(b)))
- {
- *msg = g_convert(from, -1, "UTF-16BE", "UTF-8", NULL, &msglen, &err);
- if (*msg != NULL)
- {
- *charset = AIM_CHARSET_UNICODE;
- *charsubset = 0x0000;
- *msglen_int = msglen;
- return;
- }
-
- purple_debug_error("oscar", "Conversion from UTF-8 to UTF-16BE failed: %s.\n",
- err->message);
- g_error_free(err);
- err = NULL;
- }
- }
-
- /*
- * If this is AIM then attempt to send as ISO-8859-1. If this is
- * ICQ then attempt to send as the user specified character encoding.
- */
- charsetstr = "ISO-8859-1";
- if ((destbn != NULL) && oscar_util_valid_name_icq(destbn))
- charsetstr = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
-
- /*
- * XXX - We need a way to only attempt to convert if we KNOW "from"
- * can be converted to "charsetstr"
- */
- *msg = g_convert(from, -1, charsetstr, "UTF-8", NULL, &msglen, &err);
- if (*msg != NULL) {
- *charset = AIM_CHARSET_LATIN_1;
- *charsubset = 0x0000;
- *msglen_int = msglen;
- return;
- }
-
- purple_debug_info("oscar", "Conversion from UTF-8 to %s failed (%s). Falling back to unicode.\n",
- charsetstr, err->message);
- g_error_free(err);
- err = NULL;
-
- /*
- * Nothing else worked, so send as UTF-16BE.
- */
- *msg = g_convert(from, -1, "UTF-16BE", "UTF-8", NULL, &msglen, &err);
- if (*msg != NULL) {
- *charset = AIM_CHARSET_UNICODE;
- *charsubset = 0x0000;
- *msglen_int = msglen;
- return;
- }
-
- purple_debug_error("oscar", "Error converting a Unicode message: %s\n", err->message);
- g_error_free(err);
- err = NULL;
-
- purple_debug_error("oscar", "This should NEVER happen! Sending UTF-8 text flagged as ASCII.\n");
- *msg = g_strdup(from);
- *msglen_int = strlen(*msg);
- *charset = AIM_CHARSET_ASCII;
- *charsubset = 0x0000;
- return;
-}
-
-/**
- * Looks for %n, %d, or %t in a string, and replaces them with the
- * specified name, date, and time, respectively.
- *
- * @param str The string that may contain the special variables.
- * @param name The sender name.
- *
- * @return A newly allocated string where the special variables are
- * expanded. This should be g_free'd by the caller.
- */
-static gchar *
-purple_str_sub_away_formatters(const char *str, const char *name)
-{
- char *c;
- GString *cpy;
- time_t t;
- struct tm *tme;
-
- g_return_val_if_fail(str != NULL, NULL);
- g_return_val_if_fail(name != NULL, NULL);
-
- /* Create an empty GString that is hopefully big enough for most messages */
- cpy = g_string_sized_new(1024);
-
- t = time(NULL);
- tme = localtime(&t);
-
- c = (char *)str;
- while (*c) {
- switch (*c) {
- case '%':
- if (*(c + 1)) {
- switch (*(c + 1)) {
- case 'n':
- /* append name */
- g_string_append(cpy, name);
- c++;
- break;
- case 'd':
- /* append date */
- g_string_append(cpy, purple_date_format_short(tme));
- c++;
- break;
- case 't':
- /* append time */
- g_string_append(cpy, purple_time_format(tme));
- c++;
- break;
- default:
- g_string_append_c(cpy, *c);
- }
- } else {
- g_string_append_c(cpy, *c);
- }
- break;
- default:
- g_string_append_c(cpy, *c);
- }
- c++;
- }
-
- return g_string_free(cpy, FALSE);
-}
-
-static gchar *oscar_caps_to_string(guint64 caps)
-{
- GString *str;
- const gchar *tmp;
- guint64 bit = 1;
-
- str = g_string_new("");
-
- if (!caps) {
- return NULL;
- } else while (bit <= OSCAR_CAPABILITY_LAST) {
- if (bit & caps) {
- switch (bit) {
- case OSCAR_CAPABILITY_BUDDYICON:
- tmp = _("Buddy Icon");
- break;
- case OSCAR_CAPABILITY_TALK:
- tmp = _("Voice");
- break;
- case OSCAR_CAPABILITY_DIRECTIM:
- tmp = _("AIM Direct IM");
- break;
- case OSCAR_CAPABILITY_CHAT:
- tmp = _("Chat");
- break;
- case OSCAR_CAPABILITY_GETFILE:
- tmp = _("Get File");
- break;
- case OSCAR_CAPABILITY_SENDFILE:
- tmp = _("Send File");
- break;
- case OSCAR_CAPABILITY_GAMES:
- case OSCAR_CAPABILITY_GAMES2:
- tmp = _("Games");
- break;
- case OSCAR_CAPABILITY_XTRAZ:
- case OSCAR_CAPABILITY_NEWCAPS:
- tmp = _("ICQ Xtraz");
- break;
- case OSCAR_CAPABILITY_ADDINS:
- tmp = _("Add-Ins");
- break;
- case OSCAR_CAPABILITY_SENDBUDDYLIST:
- tmp = _("Send Buddy List");
- break;
- case OSCAR_CAPABILITY_ICQ_DIRECT:
- tmp = _("ICQ Direct Connect");
- break;
- case OSCAR_CAPABILITY_APINFO:
- tmp = _("AP User");
- break;
- case OSCAR_CAPABILITY_ICQRTF:
- tmp = _("ICQ RTF");
- break;
- case OSCAR_CAPABILITY_EMPTY:
- tmp = _("Nihilist");
- break;
- case OSCAR_CAPABILITY_ICQSERVERRELAY:
- tmp = _("ICQ Server Relay");
- break;
- case OSCAR_CAPABILITY_UNICODEOLD:
- tmp = _("Old ICQ UTF8");
- break;
- case OSCAR_CAPABILITY_TRILLIANCRYPT:
- tmp = _("Trillian Encryption");
- break;
- case OSCAR_CAPABILITY_UNICODE:
- tmp = _("ICQ UTF8");
- break;
- case OSCAR_CAPABILITY_HIPTOP:
- tmp = _("Hiptop");
- break;
- case OSCAR_CAPABILITY_SECUREIM:
- tmp = _("Security Enabled");
- break;
- case OSCAR_CAPABILITY_VIDEO:
- tmp = _("Video Chat");
- break;
- /* Not actually sure about this one... WinAIM doesn't show anything */
- case OSCAR_CAPABILITY_ICHATAV:
- tmp = _("iChat AV");
- break;
- case OSCAR_CAPABILITY_LIVEVIDEO:
- tmp = _("Live Video");
- break;
- case OSCAR_CAPABILITY_CAMERA:
- tmp = _("Camera");
- break;
- case OSCAR_CAPABILITY_ICHAT_SCREENSHARE:
- tmp = _("Screen Sharing");
- break;
- default:
- tmp = NULL;
- break;
- }
- if (tmp)
- g_string_append_printf(str, "%s%s", (*(str->str) == '\0' ? "" : ", "), tmp);
- }
- bit <<= 1;
- }
-
- return g_string_free(str, FALSE);
-}
-
static char *oscar_icqstatus(int state) {
/* Make a cute little string that shows the status of the dude or dudet */
if (state & AIM_ICQ_STATE_CHAT)
@@ -816,255 +182,6 @@ static char *oscar_icqstatus(int state) {
return g_strdup(_("Online"));
}
-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_pair(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);
- g_free(utf8);
- }
-}
-
-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);
- g_free(utf8);
- }
-}
-
-/**
- * @brief Append the status information to a user_info struct
- *
- * The returned information is HTML-ready, appropriately escaped, as all information in a user_info struct should be HTML.
- *
- * @param gc The PurpleConnection
- * @param user_info A PurpleNotifyUserInfo object to which status information will be added
- * @param b The PurpleBuddy whose status is desired. This or the aim_userinfo_t (or both) must be passed to oscar_user_info_append_status().
- * @param userinfo The aim_userinfo_t of the buddy whose status is desired. This or the PurpleBuddy (or both) must be passed to oscar_user_info_append_status().
- * @param strip_html_tags If strip_html_tags is TRUE, tags embedded in the status message will be stripped, returning a non-formatted string. The string will still be HTML escaped.
- */
-static void oscar_user_info_append_status(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo, gboolean strip_html_tags)
-{
- PurpleAccount *account = purple_connection_get_account(gc);
- OscarData *od;
- PurplePresence *presence = NULL;
- PurpleStatus *status = NULL;
- gchar *message = NULL, *itmsurl = NULL, *tmp;
- gboolean is_away;
-
- od = purple_connection_get_protocol_data(gc);
-
- if (b == NULL && userinfo == NULL)
- return;
-
- if (b == NULL)
- b = purple_find_buddy(purple_connection_get_account(gc), userinfo->bn);
- else
- userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b));
-
- if (b) {
- presence = purple_buddy_get_presence(b);
- status = purple_presence_get_active_status(presence);
- }
-
- /* If we have both b and userinfo we favor userinfo, because if we're
- viewing someone's profile then we want the HTML away message, and
- the "message" attribute of the status contains only the plaintext
- message. */
- if (userinfo) {
- if ((userinfo->flags & AIM_FLAG_AWAY)
- && userinfo->away_len > 0
- && userinfo->away != NULL
- && userinfo->away_encoding != NULL)
- {
- /* Away message */
- tmp = oscar_encoding_extract(userinfo->away_encoding);
- message = oscar_encoding_to_utf8(account,
- tmp, userinfo->away, userinfo->away_len);
- g_free(tmp);
- } else {
- /*
- * Available message or non-HTML away message (because that's
- * all we have right now.
- */
- if ((userinfo->status != NULL) && userinfo->status[0] != '\0') {
- message = oscar_encoding_to_utf8(account,
- userinfo->status_encoding, userinfo->status,
- userinfo->status_len);
- }
-#if defined (_WIN32) || defined (__APPLE__)
- if (userinfo->itmsurl && (userinfo->itmsurl[0] != '\0'))
- itmsurl = oscar_encoding_to_utf8(account, userinfo->itmsurl_encoding,
- userinfo->itmsurl, userinfo->itmsurl_len);
-#endif
- }
- } else {
- message = g_strdup(purple_status_get_attr_string(status, "message"));
- itmsurl = g_strdup(purple_status_get_attr_string(status, "itmsurl"));
- }
-
- is_away = ((status && !purple_status_is_available(status)) ||
- (userinfo && (userinfo->flags & AIM_FLAG_AWAY)));
-
- if (strip_html_tags) {
- /* Away messages are HTML, but available messages were originally plain text.
- * We therefore need to strip away messages but not available messages if we're asked to remove HTML tags.
- */
- /*
- * It seems like the above comment no longer applies. All messages need
- * to be escaped.
- */
- if (message) {
- gchar *tmp2;
- tmp = purple_markup_strip_html(message);
- g_free(message);
- tmp2 = g_markup_escape_text(tmp, -1);
- g_free(tmp);
- message = tmp2;
- }
-
- } else {
- if (itmsurl) {
- tmp = g_strdup_printf("<a href=\"%s\">%s</a>",
- itmsurl, message);
- g_free(message);
- message = tmp;
- }
- }
- g_free(itmsurl);
-
- if (message) {
- tmp = purple_str_sub_away_formatters(message, purple_account_get_username(account));
- g_free(message);
- message = tmp;
- }
-
- if (b) {
- if (purple_presence_is_online(presence)) {
- if (oscar_util_valid_name_icq(purple_buddy_get_name(b)) || is_away || !message || !(*message)) {
- /* Append the status name for online ICQ statuses, away AIM statuses, and for all buddies with no message.
- * If the status name and the message are the same, only show one. */
- const char *status_name = purple_status_get_name(status);
- if (status_name && message && !strcmp(status_name, message))
- status_name = NULL;
-
- tmp = g_strdup_printf("%s%s%s",
- status_name ? status_name : "",
- ((status_name && message) && *message) ? ": " : "",
- (message && *message) ? message : "");
- g_free(message);
- message = tmp;
- }
-
- } 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 */
- tmp = g_strdup_printf("%s%s%s",
- _("Not Authorized"),
- (message && *message) ? ": " : "",
- (message && *message) ? message : "");
- g_free(message);
- message = tmp;
- } else {
- g_free(message);
- message = g_strdup(_("Offline"));
- }
- }
-
- if (presence) {
- const char *mood;
- const char *description;
- status = purple_presence_get_status(presence, "mood");
- mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME);
- description = icq_get_custom_icon_description(mood);
- if (description && *description)
- purple_notify_user_info_add_pair(user_info, _("Mood"), _(description));
- }
-
- purple_notify_user_info_add_pair(user_info, _("Status"), message);
- g_free(message);
-}
-
-static void oscar_user_info_append_extra_info(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo)
-{
- OscarData *od;
- PurpleAccount *account;
- PurplePresence *presence = NULL;
- PurpleStatus *status = NULL;
- PurpleGroup *g = NULL;
- struct buddyinfo *bi = NULL;
- char *tmp;
- const char *bname = NULL, *gname = NULL;
-
- od = purple_connection_get_protocol_data(gc);
- account = purple_connection_get_account(gc);
-
- if ((user_info == NULL) || ((b == NULL) && (userinfo == NULL)))
- return;
-
- if (userinfo == NULL)
- userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b));
-
- if (b == NULL)
- b = purple_find_buddy(account, userinfo->bn);
-
- if (b != NULL) {
- bname = purple_buddy_get_name(b);
- g = purple_buddy_get_group(b);
- gname = purple_group_get_name(g);
- presence = purple_buddy_get_presence(b);
- status = purple_presence_get_active_status(presence);
- }
-
- if (userinfo != NULL)
- bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, userinfo->bn));
-
- if ((bi != NULL) && (bi->ipaddr != 0)) {
- tmp = g_strdup_printf("%hhu.%hhu.%hhu.%hhu",
- (bi->ipaddr & 0xff000000) >> 24,
- (bi->ipaddr & 0x00ff0000) >> 16,
- (bi->ipaddr & 0x0000ff00) >> 8,
- (bi->ipaddr & 0x000000ff));
- oscar_user_info_add_pair(user_info, _("IP Address"), tmp);
- g_free(tmp);
- }
-
- 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);
- }
-
- if ((b != NULL) && (bname != NULL) && (g != NULL) && (gname != NULL)) {
- tmp = aim_ssi_getcomment(od->ssi.local, gname, bname);
- if (tmp != NULL) {
- char *tmp2 = g_markup_escape_text(tmp, strlen(tmp));
- g_free(tmp);
-
- oscar_user_info_convert_and_add_pair(account, od, user_info, _("Buddy Comment"), tmp2);
- g_free(tmp2);
- }
- }
-}
-
static char *extract_name(const char *name) {
char *tmp, *x;
int i, j;
@@ -1547,25 +664,13 @@ oscar_login(PurpleAccount *account)
oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_INCOMING, purple_parse_incoming_im, 0);
oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_MISSEDCALL, purple_parse_misses, 0);
oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_CLIENTAUTORESP, purple_parse_clientauto, 0);
- oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_ERROR, purple_parse_msgerr, 0);
oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_MTN, purple_parse_mtn, 0);
- oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_ACK, purple_parse_msgack, 0);
-#ifdef OLDSTYLE_ICQ_OFFLINEMSGS
- oscar_data_addhandler(od, SNAC_FAMILY_ICQ, SNAC_SUBTYPE_ICQ_OFFLINEMSG, purple_offlinemsg, 0);
- oscar_data_addhandler(od, SNAC_FAMILY_ICQ, SNAC_SUBTYPE_ICQ_OFFLINEMSGCOMPLETE, purple_offlinemsgdone, 0);
-#endif /* OLDSTYLE_ICQ_OFFLINEMSGS */
- oscar_data_addhandler(od, SNAC_FAMILY_ICQ, SNAC_SUBTYPE_ICQ_ALIAS, purple_icqalias, 0);
- oscar_data_addhandler(od, SNAC_FAMILY_ICQ, SNAC_SUBTYPE_ICQ_INFO, purple_icqinfo, 0);
oscar_data_addhandler(od, SNAC_FAMILY_LOCATE, SNAC_SUBTYPE_LOCATE_RIGHTSINFO, purple_parse_locaterights, 0);
- oscar_data_addhandler(od, SNAC_FAMILY_LOCATE, SNAC_SUBTYPE_LOCATE_USERINFO, purple_parse_userinfo, 0);
- oscar_data_addhandler(od, SNAC_FAMILY_LOCATE, SNAC_SUBTYPE_LOCATE_ERROR, purple_parse_locerr, 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, 0x0021, oscar_icon_req,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_OSERVICE, SNAC_SUBTYPE_OSERVICE_EVIL, purple_parse_evilnotify, 0);
oscar_data_addhandler(od, SNAC_FAMILY_POPUP, 0x0002, purple_popup, 0);
oscar_data_addhandler(od, SNAC_FAMILY_USERLOOKUP, SNAC_SUBTYPE_USERLOOKUP_ERROR, purple_parse_searcherror, 0);
oscar_data_addhandler(od, SNAC_FAMILY_USERLOOKUP, 0x0003, purple_parse_searchreply, 0);
@@ -1580,11 +685,11 @@ oscar_login(PurpleAccount *account)
return;
}
+ gc->flags |= PURPLE_CONNECTION_HTML;
if (oscar_util_valid_name_icq((purple_account_get_username(account)))) {
od->icq = TRUE;
gc->flags |= PURPLE_CONNECTION_SUPPORT_MOODS;
} else {
- gc->flags |= PURPLE_CONNECTION_HTML;
gc->flags |= PURPLE_CONNECTION_AUTO_RESP;
}
@@ -1825,34 +930,6 @@ static int purple_memrequest(OscarData *od, FlapConnection *conn, FlapFrame *fr,
AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
return 1;
}
- /* uncomment this when you're convinced it's right. remember, it's been wrong before. */
-#if 0
- if (offset > AIM_MAX_FILE_SIZE || len > AIM_MAX_FILE_SIZE) {
- char *buf;
- int i = 8;
- if (modname)
- i += strlen(modname);
- buf = g_malloc(i);
- i = 0;
- if (modname) {
- memcpy(buf, modname, strlen(modname));
- i += strlen(modname);
- }
- buf[i++] = offset & 0xff;
- buf[i++] = (offset >> 8) & 0xff;
- buf[i++] = (offset >> 16) & 0xff;
- buf[i++] = (offset >> 24) & 0xff;
- buf[i++] = len & 0xff;
- buf[i++] = (len >> 8) & 0xff;
- buf[i++] = (len >> 16) & 0xff;
- buf[i++] = (len >> 24) & 0xff;
- purple_debug_misc("oscar", "len + offset is invalid, "
- "hashing request\n");
- aim_sendmemblock(od, command->conn, offset, i, buf, AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
- g_free(buf);
- return 1;
- }
-#endif
pos = g_new0(struct pieceofcrap, 1);
pos->gc = od->gc;
@@ -2307,18 +1384,18 @@ static int purple_parse_oncoming(OscarData *od, FlapConnection *conn, FlapFrame
purple_prpl_got_user_status_deactive(account, info->bn, OSCAR_STATUS_ID_MOBILE);
}
- if (info->status != NULL && info->status[0] != '\0')
+ if (info->status != NULL && info->status[0] != '\0') {
/* Grab the available message */
- message = oscar_encoding_to_utf8(account, info->status_encoding,
- info->status, info->status_len);
+ message = oscar_encoding_to_utf8(info->status_encoding, info->status, info->status_len);
+ }
tmp2 = tmp = (message ? purple_markup_escape_text(message, -1) : NULL);
if (strcmp(status_id, OSCAR_STATUS_ID_AVAILABLE) == 0) {
- if (info->itmsurl_encoding && info->itmsurl && info->itmsurl_len)
+ if (info->itmsurl_encoding && info->itmsurl && info->itmsurl_len) {
/* Grab the iTunes Music Store URL */
- itmsurl = oscar_encoding_to_utf8(account, info->itmsurl_encoding,
- info->itmsurl, info->itmsurl_len);
+ itmsurl = oscar_encoding_to_utf8(info->itmsurl_encoding, info->itmsurl, info->itmsurl_len);
+ }
if (tmp2 == NULL && itmsurl != NULL)
/*
@@ -2424,17 +1501,11 @@ static int incomingim_chan1(OscarData *od, FlapConnection *conn, aim_userinfo_t
PurpleMessageFlags flags = 0;
struct buddyinfo *bi;
PurpleStoredImage *img;
- GString *message;
gchar *tmp;
- aim_mpmsg_section_t *curpart;
const char *start, *end;
GData *attribs;
- purple_debug_misc("oscar", "Received IM from %s with %d parts\n",
- userinfo->bn, args->mpmsg.numparts);
-
- if (args->mpmsg.numparts == 0)
- return 1;
+ purple_debug_misc("oscar", "Received IM from %s\n", userinfo->bn);
bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, userinfo->bn));
if (!bi) {
@@ -2474,38 +1545,7 @@ static int incomingim_chan1(OscarData *od, FlapConnection *conn, aim_userinfo_t
}
purple_imgstore_unref(img);
- message = g_string_new("");
- curpart = args->mpmsg.parts;
- while (curpart != NULL) {
- tmp = purple_plugin_oscar_decode_im_part(account, userinfo->bn, curpart->charset,
- curpart->charsubset, curpart->data, curpart->datalen);
- if (tmp != NULL) {
- g_string_append(message, tmp);
- g_free(tmp);
- }
-
- curpart = curpart->next;
- }
- tmp = g_string_free(message, FALSE);
-
- /*
- * If the message is from an ICQ user and to an ICQ user then escape any HTML,
- * because HTML is not sent over ICQ as a means to format a message.
- * So any HTML we receive is intended to be displayed. Also, \r\n must be
- * replaced with <br>
- *
- * Note: There *may* be some clients which send messages as HTML formatted -
- * they need to be special-cased somehow.
- */
- if (od->icq && oscar_util_valid_name_icq(userinfo->bn)) {
- /* being recevied by ICQ from ICQ - escape HTML so it is displayed as sent */
- gchar *tmp2 = g_markup_escape_text(tmp, -1);
- g_free(tmp);
- tmp = tmp2;
- tmp2 = purple_strreplace(tmp, "\r\n", "<br>");
- g_free(tmp);
- tmp = tmp2;
- }
+ tmp = g_strdup(args->msg);
/*
* Convert iChat color tags to normal font tags.
@@ -2589,8 +1629,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));
+ serv_got_im(gc, userinfo->bn, tmp, flags, (args->icbmflags & AIM_IMFLAGS_OFFLINE) ? args->timestamp : time(NULL));
g_free(tmp);
return 1;
@@ -2618,35 +1657,20 @@ incomingim_chan2(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo,
G_GUINT64_FORMAT ", user %s, status %hu\n",
args->type, userinfo->bn, args->status);
- if (args->msg != NULL)
- {
- if (args->encoding != NULL)
- {
- char *encoding = NULL;
- encoding = oscar_encoding_extract(args->encoding);
- message = oscar_encoding_to_utf8(account, encoding, args->msg,
- args->msglen);
- g_free(encoding);
- } else {
- if (g_utf8_validate(args->msg, args->msglen, NULL))
- message = g_strdup(args->msg);
- }
+ if (args->msg != NULL) {
+ message = oscar_encoding_to_utf8(args->encoding, args->msg, args->msglen);
}
if (args->type & OSCAR_CAPABILITY_CHAT)
{
- char *encoding, *utf8name, *tmp;
+ char *utf8name, *tmp;
GHashTable *components;
if (!args->info.chat.roominfo.name || !args->info.chat.roominfo.exchange) {
g_free(message);
return 1;
}
- encoding = args->encoding ? oscar_encoding_extract(args->encoding) : NULL;
- utf8name = oscar_encoding_to_utf8(account, encoding,
- args->info.chat.roominfo.name,
- args->info.chat.roominfo.namelen);
- g_free(encoding);
+ utf8name = oscar_encoding_to_utf8(args->encoding, args->info.chat.roominfo.name, args->info.chat.roominfo.namelen);
tmp = extract_name(utf8name);
if (tmp != NULL)
@@ -2667,8 +1691,7 @@ incomingim_chan2(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo,
components);
}
- else if ((args->type & OSCAR_CAPABILITY_SENDFILE) ||
- (args->type & OSCAR_CAPABILITY_DIRECTIM))
+ else if ((args->type & OSCAR_CAPABILITY_SENDFILE) || (args->type & OSCAR_CAPABILITY_DIRECTIM))
{
if (args->status == AIM_RENDEZVOUS_PROPOSE)
{
@@ -2676,7 +1699,7 @@ incomingim_chan2(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo,
}
else if (args->status == AIM_RENDEZVOUS_CANCEL)
{
- /* The other user canceled a peer request */
+ /* The other user cancelled a peer request */
PeerConnection *conn;
conn = peer_connection_find_by_cookie(od, userinfo->bn, args->cookie);
@@ -2721,29 +1744,27 @@ incomingim_chan2(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo,
purple_debug_info("oscar", "Got an ICQ Server Relay message of "
"type %d\n", args->info.rtfmsg.msgtype);
- if (args->info.rtfmsg.msgtype == 1)
- {
- if (args->info.rtfmsg.rtfmsg != NULL)
- {
- char *rtfmsg = NULL;
- if (args->encoding != NULL) {
- char *encoding = oscar_encoding_extract(args->encoding);
- rtfmsg = oscar_encoding_to_utf8(account, encoding,
- args->info.rtfmsg.rtfmsg, strlen(args->info.rtfmsg.rtfmsg));
- g_free(encoding);
- } else {
- if (g_utf8_validate(args->info.rtfmsg.rtfmsg, strlen(args->info.rtfmsg.rtfmsg), NULL))
- rtfmsg = g_strdup(args->info.rtfmsg.rtfmsg);
- }
- if (rtfmsg) {
- serv_got_im(gc, userinfo->bn, rtfmsg, flags, time(NULL));
- g_free(rtfmsg);
- }
+ if (args->info.rtfmsg.msgtype == 1) {
+ if (args->info.rtfmsg.msg != NULL) {
+ char *rtfmsg = oscar_encoding_to_utf8(args->encoding, args->info.rtfmsg.msg, strlen(args->info.rtfmsg.msg));
+ char *tmp, *tmp2;
+
+ /* Channel 2 messages are supposed to be plain-text (never mind the name "rtfmsg", even
+ * the official client doesn't parse them as RTF). Therefore, we should escape them before
+ * showing to the user. */
+ tmp = g_markup_escape_text(rtfmsg, -1);
+ g_free(rtfmsg);
+ tmp2 = purple_strreplace(tmp, "\r\n", "<br>");
+ g_free(tmp);
+
+ serv_got_im(gc, userinfo->bn, tmp2, flags, time(NULL));
+ aim_im_send_icq_confirmation(od, userinfo->bn, args->cookie);
+ g_free(tmp2);
}
- } else if (args->info.rtfmsg.msgtype == 26)
+ } else if (args->info.rtfmsg.msgtype == 26) {
purple_debug_info("oscar", "Sending X-Status Reply\n");
icq_relay_xstatus(od, userinfo->bn, args->cookie);
-
+ }
}
else
{
@@ -2756,122 +1777,6 @@ incomingim_chan2(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo,
return 1;
}
-/*
- * Authorization Functions
- * Most of these are callbacks from dialogs. They're used by both
- * methods of authorization (SSI and old-school channel 4 ICBM)
- */
-/* When you ask other people for authorization */
-static void
-purple_auth_request(struct name_data *data, char *msg)
-{
- PurpleConnection *gc;
- OscarData *od;
- PurpleAccount *account;
- PurpleBuddy *buddy;
- PurpleGroup *group;
- const char *bname, *gname;
-
- gc = data->gc;
- od = purple_connection_get_protocol_data(gc);
- account = purple_connection_get_account(gc);
- buddy = purple_find_buddy(account, data->name);
- if (buddy != NULL)
- group = purple_buddy_get_group(buddy);
- else
- group = NULL;
-
- if (group != NULL)
- {
- bname = purple_buddy_get_name(buddy);
- gname = purple_group_get_name(group);
- purple_debug_info("oscar", "ssi: adding buddy %s to group %s\n",
- bname, gname);
- aim_ssi_sendauthrequest(od, data->name, 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))
- {
- aim_ssi_addbuddy(od, bname, gname, NULL, purple_buddy_get_alias_only(buddy), NULL, NULL, TRUE);
-
- /* Mobile users should always be online */
- if (bname[0] == '+') {
- purple_prpl_got_user_status(account,
- purple_buddy_get_name(buddy),
- OSCAR_STATUS_ID_AVAILABLE, NULL);
- purple_prpl_got_user_status(account,
- purple_buddy_get_name(buddy),
- OSCAR_STATUS_ID_MOBILE, NULL);
- }
- }
- }
-
- oscar_free_name_data(data);
-}
-
-static void
-purple_auth_sendrequest(PurpleConnection *gc, const char *name)
-{
- struct name_data *data;
-
- data = g_new0(struct name_data, 1);
- data->gc = gc;
- data->name = g_strdup(name);
-
- purple_request_input(data->gc, NULL, _("Authorization Request Message:"),
- NULL, _("Please authorize me!"), TRUE, FALSE, NULL,
- _("_OK"), G_CALLBACK(purple_auth_request),
- _("_Cancel"), G_CALLBACK(oscar_free_name_data),
- purple_connection_get_account(gc), name, NULL,
- data);
-}
-
-static void
-purple_auth_sendrequest_menu(PurpleBlistNode *node, gpointer ignored)
-{
- PurpleBuddy *buddy;
- PurpleConnection *gc;
-
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
-
- buddy = (PurpleBuddy *) node;
- gc = purple_account_get_connection(purple_buddy_get_account(buddy));
- purple_auth_sendrequest(gc, purple_buddy_get_name(buddy));
-}
-
-/* When other people ask you for authorization */
-static void
-purple_auth_grant(gpointer cbdata)
-{
- struct name_data *data = cbdata;
- PurpleConnection *gc = data->gc;
- OscarData *od = purple_connection_get_protocol_data(gc);
-
- aim_ssi_sendauthreply(od, data->name, 0x01, NULL);
-
- oscar_free_name_data(data);
-}
-
-/* When other people ask you for authorization */
-static void
-purple_auth_dontgrant(struct name_data *data, char *msg)
-{
- PurpleConnection *gc = data->gc;
- OscarData *od = purple_connection_get_protocol_data(gc);
-
- aim_ssi_sendauthreply(od, data->name, 0x00, msg ? msg : _("No reason given."));
-}
-
-static void
-purple_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(purple_auth_dontgrant),
- _("_Cancel"), G_CALLBACK(oscar_free_name_data),
- purple_connection_get_account(data->gc), data->name, NULL,
- data);
-}
-
/* When someone sends you buddies */
static void
purple_icq_buddyadd(struct name_data *data)
@@ -2915,7 +1820,7 @@ incomingim_chan4(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo,
purple_str_strip_char(msg1[i], '\r');
/* TODO: Should use an encoding other than ASCII? */
- msg2[i] = purple_plugin_oscar_decode_im_part(account, uin, AIM_CHARSET_ASCII, 0x0000, msg1[i], strlen(msg1[i]));
+ msg2[i] = oscar_decode_im(account, uin, AIM_CHARSET_ASCII, msg1[i], strlen(msg1[i]));
g_free(uin);
}
msg2[i] = NULL;
@@ -2968,24 +1873,17 @@ incomingim_chan4(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo,
case 0x06: { /* Someone requested authorization */
if (i >= 6) {
- struct name_data *data = g_new(struct name_data, 1);
gchar *bn = g_strdup_printf("%u", args->uin);
gchar *reason = NULL;
if (msg2[5] != NULL)
- reason = purple_plugin_oscar_decode_im_part(account, bn, AIM_CHARSET_LATIN_1, 0x0000, msg2[5], strlen(msg2[5]));
+ reason = oscar_decode_im(account, bn, AIM_CHARSET_LATIN_1, msg2[5], strlen(msg2[5]));
purple_debug_info("oscar",
"Received an authorization request from UIN %u\n",
args->uin);
- data->gc = gc;
- data->name = bn;
- data->nick = NULL;
-
- purple_account_request_authorization(account, bn, NULL, NULL,
- reason, purple_find_buddy(account, bn) != NULL,
- purple_auth_grant,
- purple_auth_dontgrant_msgprompt, data);
+ aim_icq_getalias(od, bn, TRUE, reason);
+ g_free(bn);
g_free(reason);
}
} break;
@@ -3087,7 +1985,8 @@ incomingim_chan4(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo,
case 0x1a: { /* Handle SMS or someone has sent you a greeting card or requested buddies? */
ByteStream qbs;
- int smstype, taglen, smslen;
+ guint16 smstype;
+ guint32 taglen, smslen;
char *tagstr = NULL, *smsmsg = NULL;
xmlnode *xmlroot = NULL, *xmltmp = NULL;
gchar *uin = NULL, *message = NULL;
@@ -3101,12 +2000,23 @@ incomingim_chan4(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo,
if (smstype != 0)
break;
taglen = byte_stream_getle32(&qbs);
+ if (taglen > 2000) {
+ /* Avoid trying to allocate large amounts of memory, in
+ case we get something unexpected. */
+ break;
+ }
tagstr = byte_stream_getstr(&qbs, taglen);
if (tagstr == NULL)
break;
byte_stream_advance(&qbs, 3);
byte_stream_advance(&qbs, 4);
smslen = byte_stream_getle32(&qbs);
+ if (smslen > 2000) {
+ /* Avoid trying to allocate large amounts of memory, in
+ case we get something unexpected. */
+ g_free(tagstr);
+ break;
+ }
smsmsg = byte_stream_getstr(&qbs, smslen);
/* Check if this is an SMS being sent from server */
@@ -3393,67 +2303,8 @@ static int purple_parse_genericerr(OscarData *od, FlapConnection *conn, FlapFram
reason = (guint16) va_arg(ap, unsigned int);
va_end(ap);
- purple_debug_error("oscar",
- "snac threw error (reason 0x%04hx: %s)\n", reason,
- (reason < msgerrreasonlen) ? msgerrreason[reason] : "unknown");
- return 1;
-}
-
-static int purple_parse_msgerr(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- PurpleConnection *gc = od->gc;
-#ifdef TODOFT
- OscarData *od = purple_connection_get_protocol_data(gc);
- PurpleXfer *xfer;
-#endif
- va_list ap;
- guint16 reason, errcode;
- char *data, *reason_str, *buf;
-
- va_start(ap, fr);
- reason = (guint16)va_arg(ap, unsigned int);
- errcode = (guint16)va_arg(ap, unsigned int);
- data = va_arg(ap, char *);
- va_end(ap);
-
- purple_debug_error("oscar",
- "Message error with data %s and reason %hu and errcode %hu\n",
- (data != NULL ? data : ""), reason, errcode);
-
- if ((data == NULL) || (*data == '\0'))
- /* We can't do anything if data is empty */
- return 1;
-
-#ifdef TODOFT
- /* If this was a file transfer request, data is a cookie */
- if ((xfer = oscar_find_xfer_by_cookie(od->file_transfers, data))) {
- purple_xfer_cancel_remote(xfer);
- return 1;
- }
-#endif
-
- /* Data is assumed to be the destination bn */
-
- reason_str = g_strdup((reason < msgerrreasonlen) ? _(msgerrreason[reason]) : _("Unknown reason"));
- if (errcode != 0 && errcode < errcodereasonlen)
- buf = g_strdup_printf(_("Unable to send message: %s (%s)"), reason_str,
- _(errcodereason[errcode]));
- else
- buf = g_strdup_printf(_("Unable to send message: %s"), reason_str);
-
- if (!purple_conv_present_error(data, 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)"),
- data ? data : "(unknown)", reason_str,
- _(errcodereason[errcode]));
- else
- buf = g_strdup_printf(_("Unable to send message to %s: %s"),
- data ? data : "(unknown)", reason_str);
- purple_notify_error(od->gc, NULL, buf, reason_str);
- }
- g_free(buf);
- g_free(reason_str);
-
+ purple_debug_error("oscar", "snac threw error (reason 0x%04hx: %s)\n",
+ reason, oscar_get_msgerr_reason(reason));
return 1;
}
@@ -3496,104 +2347,6 @@ static int purple_parse_mtn(OscarData *od, FlapConnection *conn, FlapFrame *fr,
return 1;
}
-/*
- * We get this error when there was an error in the locate family. This
- * happens when you request info of someone who is offline.
- */
-static int purple_parse_locerr(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- gchar *buf;
- va_list ap;
- guint16 reason;
- char *destn;
- PurpleNotifyUserInfo *user_info;
-
- va_start(ap, fr);
- reason = (guint16) va_arg(ap, unsigned int);
- destn = va_arg(ap, char *);
- va_end(ap);
-
- if (destn == NULL)
- return 1;
-
- user_info = purple_notify_user_info_new();
- buf = g_strdup_printf(_("User information not available: %s"), (reason < msgerrreasonlen) ? _(msgerrreason[reason]) : _("Unknown reason."));
- purple_notify_user_info_add_pair(user_info, NULL, buf);
- purple_notify_userinfo(od->gc, destn, user_info, NULL, NULL);
- purple_notify_user_info_destroy(user_info);
- purple_conv_present_error(destn, purple_connection_get_account(od->gc), buf);
- g_free(buf);
-
- return 1;
-}
-
-static int purple_parse_userinfo(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- PurpleConnection *gc = od->gc;
- PurpleAccount *account = purple_connection_get_account(gc);
- PurpleNotifyUserInfo *user_info;
- gchar *tmp = NULL, *info_utf8 = NULL;
- va_list ap;
- aim_userinfo_t *userinfo;
-
- va_start(ap, fr);
- userinfo = va_arg(ap, aim_userinfo_t *);
- va_end(ap);
-
- user_info = purple_notify_user_info_new();
-
- oscar_user_info_append_status(gc, user_info, /* PurpleBuddy */ NULL, userinfo, /* strip_html_tags */ FALSE);
-
- 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);
- g_free(tmp);
- }
-
- oscar_user_info_append_extra_info(gc, user_info, NULL, 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)));
- }
-
- 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)));
- }
-
- if (userinfo->capabilities != 0) {
- tmp = oscar_caps_to_string(userinfo->capabilities);
- oscar_user_info_add_pair(user_info, _("Capabilities"), tmp);
- g_free(tmp);
- }
-
- /* Info */
- if ((userinfo->info_len > 0) && (userinfo->info != NULL) && (userinfo->info_encoding != NULL)) {
- tmp = oscar_encoding_extract(userinfo->info_encoding);
- info_utf8 = oscar_encoding_to_utf8(account, tmp, userinfo->info,
- userinfo->info_len);
- g_free(tmp);
- if (info_utf8 != NULL) {
- tmp = purple_str_sub_away_formatters(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);
- g_free(tmp);
- g_free(info_utf8);
- }
- }
-
- purple_notify_user_info_add_section_break(user_info);
- tmp = g_strdup_printf("<a href=\"http://profiles.aim.com/%s\">%s</a>",
- purple_normalize(account, userinfo->bn), _("View web profile"));
- purple_notify_user_info_add_pair(user_info, NULL, tmp);
- g_free(tmp);
-
- purple_notify_userinfo(gc, userinfo->bn, user_info, NULL, NULL);
- purple_notify_user_info_destroy(user_info);
-
- return 1;
-}
-
static int purple_parse_motd(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
{
char *msg;
@@ -3736,13 +2489,7 @@ static int purple_conv_chat_leave(OscarData *od, FlapConnection *conn, FlapFrame
static int purple_conv_chat_info_update(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
va_list ap;
- aim_userinfo_t *userinfo;
- struct aim_chat_roominfo *roominfo;
- char *roomname;
- int usercount;
- char *roomdesc;
- guint16 unknown_c9, unknown_d2, unknown_d5, maxmsglen, maxvisiblemsglen;
- guint32 creationtime;
+ guint16 maxmsglen, maxvisiblemsglen;
PurpleConnection *gc = od->gc;
struct chat_connection *ccon = find_oscar_chat_by_conn(gc, conn);
@@ -3750,16 +2497,7 @@ static int purple_conv_chat_info_update(OscarData *od, FlapConnection *conn, Fla
return 1;
va_start(ap, fr);
- roominfo = va_arg(ap, struct aim_chat_roominfo *);
- roomname = va_arg(ap, char *);
- usercount= va_arg(ap, int);
- userinfo = va_arg(ap, aim_userinfo_t *);
- roomdesc = va_arg(ap, char *);
- unknown_c9 = (guint16)va_arg(ap, unsigned int);
- creationtime = va_arg(ap, guint32);
maxmsglen = (guint16)va_arg(ap, unsigned int);
- unknown_d2 = (guint16)va_arg(ap, unsigned int);
- unknown_d5 = (guint16)va_arg(ap, unsigned int);
maxvisiblemsglen = (guint16)va_arg(ap, unsigned int);
va_end(ap);
@@ -3775,7 +2513,6 @@ static int purple_conv_chat_info_update(OscarData *od, FlapConnection *conn, Fla
static int purple_conv_chat_incoming_msg(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
PurpleConnection *gc = od->gc;
- PurpleAccount *account = purple_connection_get_account(gc);
struct chat_connection *ccon = find_oscar_chat_by_conn(gc, conn);
gchar *utf8;
va_list ap;
@@ -3794,10 +2531,7 @@ static int purple_conv_chat_incoming_msg(OscarData *od, FlapConnection *conn, Fl
charset = va_arg(ap, char *);
va_end(ap);
- utf8 = oscar_encoding_to_utf8(account, charset, msg, len);
- if (utf8 == NULL)
- /* The conversion failed! */
- utf8 = g_strdup(_("[Unable to display a message from this user because it contained invalid characters.]"));
+ utf8 = oscar_encoding_to_utf8(charset, msg, len);
serv_got_chat_in(gc, ccon->id, info->bn, 0, utf8, time(NULL));
g_free(utf8);
@@ -3915,41 +2649,6 @@ purple_icons_fetch(PurpleConnection *gc)
purple_debug_misc("oscar", "no more icons to request\n");
}
-/*
- * Received in response to an IM sent with the AIM_IMFLAGS_ACK option.
- */
-static int purple_parse_msgack(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- va_list ap;
- guint16 type;
- char *bn;
-
- va_start(ap, fr);
- type = (guint16) va_arg(ap, unsigned int);
- bn = va_arg(ap, char *);
- va_end(ap);
-
- purple_debug_info("oscar", "Sent message to %s.\n", bn);
-
- return 1;
-}
-
-static int purple_parse_evilnotify(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
-#ifdef CRAZY_WARNING
- va_list ap;
- guint16 newevil;
- aim_userinfo_t *userinfo;
-
- va_start(ap, fr);
- newevil = (guint16) va_arg(ap, unsigned int);
- userinfo = va_arg(ap, aim_userinfo_t *);
- va_end(ap);
-
- purple_prpl_got_account_warning_level(account, (userinfo && userinfo->bn) ? userinfo->bn : NULL, (newevil/10.0) + 0.5);
-#endif
-
- return 1;
-}
-
static int purple_selfinfo(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
int warning_level;
va_list ap;
@@ -3970,10 +2669,6 @@ static int purple_selfinfo(OscarData *od, FlapConnection *conn, FlapFrame *fr, .
*/
warning_level = info->warnlevel/10.0 + 0.5;
-#ifdef CRAZY_WARNING
- purple_presence_set_warning_level(presence, warning_level);
-#endif
-
return 1;
}
@@ -4112,16 +2807,14 @@ static int purple_bosrights(OscarData *od, FlapConnection *conn, FlapFrame *fr,
tmp = purple_markup_strip_html(message);
itmsurl = purple_status_get_attr_string(status, "itmsurl");
aim_srv_setextrainfo(od, FALSE, 0, is_available, tmp, itmsurl);
+ aim_srv_set_dc_info(od);
g_free(tmp);
presence = purple_status_get_presence(status);
aim_srv_setidle(od, !purple_presence_is_idle(presence) ? 0 : time(NULL) - purple_presence_get_idle_time(presence));
if (od->icq) {
-#ifdef OLDSTYLE_ICQ_OFFLINEMSGS
- aim_icq_reqofflinemsgs(od);
-#endif
- oscar_set_extendedstatus(gc);
+ oscar_set_extended_status(gc);
aim_icq_setsecurity(od,
purple_account_get_bool(account, "authorization", OSCAR_DEFAULT_AUTHORIZATION),
purple_account_get_bool(account, "web_aware", OSCAR_DEFAULT_WEB_AWARE));
@@ -4153,206 +2846,6 @@ static int purple_bosrights(OscarData *od, FlapConnection *conn, FlapFrame *fr,
return 1;
}
-#ifdef OLDSTYLE_ICQ_OFFLINEMSGS
-static int purple_offlinemsg(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- va_list ap;
- struct aim_icq_offlinemsg *msg;
- struct aim_incomingim_ch4_args args;
- time_t t;
-
- va_start(ap, fr);
- msg = va_arg(ap, struct aim_icq_offlinemsg *);
- va_end(ap);
-
- purple_debug_info("oscar",
- "Received offline message. Converting to channel 4 ICBM...\n");
- args.uin = msg->sender;
- args.type = msg->type;
- args.flags = msg->flags;
- args.msglen = msg->msglen;
- args.msg = msg->msg;
- t = purple_time_build(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
- incomingim_chan4(od, conn, NULL, &args, t);
-
- return 1;
-}
-
-static int purple_offlinemsgdone(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
-{
- aim_icq_ackofflinemsgs(od);
- return 1;
-}
-#endif /* OLDSTYLE_ICQ_OFFLINEMSGS */
-
-static int purple_icqinfo(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
-{
- PurpleConnection *gc;
- PurpleAccount *account;
- PurpleBuddy *buddy;
- struct buddyinfo *bi;
- gchar who[16];
- PurpleNotifyUserInfo *user_info;
- gchar *utf8;
- gchar *buf;
- const gchar *alias;
- va_list ap;
- struct aim_icq_info *info;
-
- gc = od->gc;
- account = purple_connection_get_account(gc);
-
- va_start(ap, fr);
- info = va_arg(ap, struct aim_icq_info *);
- va_end(ap);
-
- if (!info->uin)
- return 0;
-
- user_info = purple_notify_user_info_new();
-
- g_snprintf(who, sizeof(who), "%u", info->uin);
- buddy = purple_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);
- oscar_user_info_convert_and_add(account, od, user_info, _("Nick"), info->nick);
- if ((bi != NULL) && (bi->ipaddr != 0)) {
- char *tstr = g_strdup_printf("%hhu.%hhu.%hhu.%hhu",
- (bi->ipaddr & 0xff000000) >> 24,
- (bi->ipaddr & 0x00ff0000) >> 16,
- (bi->ipaddr & 0x0000ff00) >> 8,
- (bi->ipaddr & 0x000000ff));
- purple_notify_user_info_add_pair(user_info, _("IP Address"), tstr);
- g_free(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);
- if (info->email && info->email[0] && (utf8 = oscar_utf8_try_convert(account, od, info->email))) {
- buf = g_strdup_printf("<a href=\"mailto:%s\">%s</a>", utf8, utf8);
- purple_notify_user_info_add_pair(user_info, _("Email Address"), buf);
- g_free(buf);
- g_free(utf8);
- }
- if (info->numaddresses && info->email2) {
- int i;
- for (i = 0; i < info->numaddresses; i++) {
- if (info->email2[i] && info->email2[i][0] && (utf8 = oscar_utf8_try_convert(account, od, info->email2[i]))) {
- buf = g_strdup_printf("<a href=\"mailto:%s\">%s</a>", utf8, utf8);
- purple_notify_user_info_add_pair(user_info, _("Email Address"), buf);
- g_free(buf);
- g_free(utf8);
- }
- }
- }
- 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")));
-
- if ((info->birthyear > 1900) && (info->birthmonth > 0) && (info->birthday > 0)) {
- /* Initialize the struct properly or strftime() will crash
- * under some conditions (e.g. Debian sarge w/ LANG=en_HK). */
- time_t t = time(NULL);
- struct tm *tm = localtime(&t);
-
- tm->tm_mday = (int)info->birthday;
- tm->tm_mon = (int)info->birthmonth - 1;
- tm->tm_year = (int)info->birthyear - 1900;
-
- /* To be 100% sure that the fields are re-normalized.
- * If you're sure strftime() ALWAYS does this EVERYWHERE,
- * feel free to remove it. --rlaager */
- mktime(tm);
-
- oscar_user_info_convert_and_add(account, od, user_info, _("Birthday"), purple_date_format_short(tm));
- }
- 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);
- }
- if (info->personalwebpage && info->personalwebpage[0] && (utf8 = oscar_utf8_try_convert(account, od, info->personalwebpage))) {
- buf = g_strdup_printf("<a href=\"%s\">%s</a>", utf8, utf8);
- purple_notify_user_info_add_pair(user_info, _("Personal Web Page"), buf);
- g_free(buf);
- g_free(utf8);
- }
-
- if (buddy != NULL)
- oscar_user_info_append_status(gc, user_info, buddy, /* aim_userinfo_t */ NULL, /* strip_html_tags */ FALSE);
-
- oscar_user_info_convert_and_add(account, od, user_info, _("Additional Information"), info->info);
- purple_notify_user_info_add_section_break(user_info);
-
- if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) {
- purple_notify_user_info_add_section_header(user_info, _("Home Address"));
-
- oscar_user_info_convert_and_add(account, od, user_info, _("Address"), info->homeaddr);
- oscar_user_info_convert_and_add(account, od, user_info, _("City"), info->homecity);
- oscar_user_info_convert_and_add(account, od, user_info, _("State"), info->homestate);
- oscar_user_info_convert_and_add(account, od, user_info, _("Zip Code"), info->homezip);
- }
- if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
- purple_notify_user_info_add_section_header(user_info, _("Work Address"));
-
- oscar_user_info_convert_and_add(account, od, user_info, _("Address"), info->workaddr);
- oscar_user_info_convert_and_add(account, od, user_info, _("City"), info->workcity);
- oscar_user_info_convert_and_add(account, od, user_info, _("State"), info->workstate);
- oscar_user_info_convert_and_add(account, od, user_info, _("Zip Code"), info->workzip);
- }
- if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
- purple_notify_user_info_add_section_header(user_info, _("Work Information"));
-
- 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);
-
- if (info->workwebpage && info->workwebpage[0] && (utf8 = oscar_utf8_try_convert(account, od, info->workwebpage))) {
- char *webpage = g_strdup_printf("<a href=\"%s\">%s</a>", utf8, utf8);
- purple_notify_user_info_add_pair(user_info, _("Web Page"), webpage);
- g_free(webpage);
- g_free(utf8);
- }
- }
-
- if (buddy != NULL)
- alias = purple_buddy_get_alias(buddy);
- else
- alias = who;
- purple_notify_userinfo(gc, who, user_info, NULL, NULL);
- purple_notify_user_info_destroy(user_info);
-
- return 1;
-}
-
-static int purple_icqalias(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
-{
- PurpleConnection *gc = od->gc;
- PurpleAccount *account = purple_connection_get_account(gc);
- gchar who[16], *utf8;
- PurpleBuddy *b;
- va_list ap;
- struct aim_icq_info *info;
-
- va_start(ap, fr);
- info = va_arg(ap, struct aim_icq_info *);
- va_end(ap);
-
- if (info->uin && info->nick && info->nick[0] && (utf8 = oscar_utf8_try_convert(account, od, info->nick))) {
- g_snprintf(who, sizeof(who), "%u", info->uin);
- serv_got_alias(gc, who, utf8);
- if ((b = purple_find_buddy(account, who))) {
- purple_blist_node_set_string((PurpleBlistNode*)b, "servernick", utf8);
- }
- g_free(utf8);
- }
-
- return 1;
-}
-
static int purple_popup(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
{
PurpleConnection *gc = od->gc;
@@ -4577,8 +3070,8 @@ purple_odc_send_im(PeerConnection *conn, const char *message, PurpleMessageFlags
GString *msg;
GString *data;
gchar *tmp;
- int tmplen;
- guint16 charset, charsubset;
+ gsize tmplen;
+ guint16 charset;
GData *attribs;
const char *start, *end, *last;
int oscar_id = 0;
@@ -4639,8 +3132,7 @@ purple_odc_send_im(PeerConnection *conn, const char *message, PurpleMessageFlags
g_string_append(msg, "</BODY></HTML>");
/* Convert the message to a good encoding */
- purple_plugin_oscar_convert_to_best_encoding(conn->od->gc,
- conn->bn, msg->str, &tmp, &tmplen, &charset, &charsubset);
+ tmp = oscar_encode_im(msg->str, &tmplen, &charset, NULL);
g_string_free(msg, TRUE);
msg = g_string_new_len(tmp, tmplen);
g_free(tmp);
@@ -4687,7 +3179,7 @@ oscar_send_im(PurpleConnection *gc, const char *name, const char *message, Purpl
}
if (imflags & PURPLE_MESSAGE_AUTO_RESP)
- tmp1 = purple_str_sub_away_formatters(message, name);
+ tmp1 = oscar_util_format_string(message, name);
else
tmp1 = g_strdup(message);
@@ -4720,26 +3212,14 @@ oscar_send_im(PurpleConnection *gc, const char *name, const char *message, Purpl
g_hash_table_insert(od->buddyinfo, g_strdup(purple_normalize(account, name)), bi);
}
- args.flags = AIM_IMFLAGS_ACK | AIM_IMFLAGS_CUSTOMFEATURES;
+ args.flags = 0;
if (!is_sms && (!buddy || !PURPLE_BUDDY_IS_ONLINE(buddy)))
args.flags |= AIM_IMFLAGS_OFFLINE;
if (od->icq) {
- /* We have to present different "features" (whose meaning
- is unclear and are merely a result of protocol inspection)
- to offline ICQ buddies. Otherwise, the official
- ICQ client doesn't treat those messages as being "ANSI-
- encoded" (and instead, assumes them to be UTF-8).
- For more details, see SF issue 1179452.
- */
- if (buddy && PURPLE_BUDDY_IS_ONLINE(buddy)) {
- args.features = features_icq;
- args.featureslen = sizeof(features_icq);
- } else {
- args.features = features_icq_offline;
- args.featureslen = sizeof(features_icq_offline);
- }
+ args.features = features_icq;
+ args.featureslen = sizeof(features_icq);
} else {
args.features = features_aim;
args.featureslen = sizeof(features_aim);
@@ -4789,32 +3269,19 @@ oscar_send_im(PurpleConnection *gc, const char *name, const char *message, Purpl
args.destbn = name;
- /*
- * If we're IMing an SMS user or an ICQ user from an ICQ account, then strip HTML.
- */
if (oscar_util_valid_name_sms(name)) {
- /* Messaging an SMS (mobile) user */
+ /* Messaging an SMS (mobile) user--strip HTML */
tmp2 = purple_markup_strip_html(tmp1);
is_html = FALSE;
- } else if (od->icq) {
- if (oscar_util_valid_name_icq(name)) {
- /* From ICQ to ICQ */
- tmp2 = purple_markup_strip_html(tmp1);
- is_html = FALSE;
- } else {
- /* From ICQ to AIM */
- tmp2 = g_strdup(tmp1);
- is_html = TRUE;
- }
} else {
- /* From AIM to AIM and AIM to ICQ */
- tmp2 = g_strdup(tmp1);
+ /* ICQ 6 wants its HTML wrapped in these tags. Oblige it. */
+ tmp2 = g_strdup_printf("<HTML><BODY>%s</BODY></HTML>", tmp1);
is_html = TRUE;
}
g_free(tmp1);
tmp1 = tmp2;
- purple_plugin_oscar_convert_to_best_encoding(gc, name, tmp1, (char **)&args.msg, &args.msglen, &args.charset, &args.charsubset);
+ args.msg = oscar_encode_im(tmp1, &args.msglen, &args.charset, NULL);
if (is_html && (args.msglen > MAXMSGLEN)) {
/* If the length was too long, try stripping the HTML and then running it back through
* purple_strdup_withhtml() and the encoding process. The result may be shorter. */
@@ -4831,14 +3298,12 @@ oscar_send_im(PurpleConnection *gc, const char *name, const char *message, Purpl
g_free(tmp1);
tmp1 = tmp2;
- purple_plugin_oscar_convert_to_best_encoding(gc, name, tmp1, (char **)&args.msg, &args.msglen, &args.charset, &args.charsubset);
-
+ args.msg = oscar_encode_im(tmp1, &args.msglen, &args.charset, NULL);
purple_debug_info("oscar", "Sending %s as %s because the original was too long.\n",
message, (char *)args.msg);
}
- purple_debug_info("oscar", "Sending IM, charset=0x%04hx, charsubset=0x%04hx, length=%d\n",
- args.charset, args.charsubset, args.msglen);
+ purple_debug_info("oscar", "Sending IM, charset=0x%04hx, length=%" G_GSIZE_FORMAT "\n", args.charset, args.msglen);
ret = aim_im_sendch1_ext(od, &args);
g_free((char *)args.msg);
}
@@ -4865,43 +3330,11 @@ void oscar_get_info(PurpleConnection *gc, const char *name) {
aim_locate_getinfoshort(od, name, 0x00000003);
}
-#if 0
-static void oscar_set_dir(PurpleConnection *gc, const char *first, const char *middle, const char *last,
- const char *maiden, const char *city, const char *state, const char *country, int web) {
- /* XXX - some of these things are wrong, but i'm lazy */
- OscarData *od = purple_connection_get_protocol_data(gc);
- aim_locate_setdirinfo(od, first, middle, last,
- maiden, NULL, NULL, city, state, NULL, 0, web);
-}
-#endif
-
void oscar_set_idle(PurpleConnection *gc, int time) {
OscarData *od = purple_connection_get_protocol_data(gc);
aim_srv_setidle(od, time);
}
-static
-gchar *purple_prpl_oscar_convert_to_infotext(const gchar *str, gsize *ret_len, char **encoding)
-{
- int charset = 0;
- char *encoded = NULL;
-
- charset = oscar_charset_check(str);
- if (charset == AIM_CHARSET_UNICODE) {
- encoded = g_convert(str, -1, "UTF-16BE", "UTF-8", NULL, ret_len, NULL);
- *encoding = "unicode-2-0";
- } else if (charset == AIM_CHARSET_LATIN_1) {
- encoded = g_convert(str, -1, "ISO-8859-1", "UTF-8", NULL, ret_len, NULL);
- *encoding = "iso-8859-1";
- } else {
- encoded = g_strdup(str);
- *ret_len = strlen(str);
- *encoding = "us-ascii";
- }
-
- return encoded;
-}
-
void
oscar_set_info(PurpleConnection *gc, const char *rawinfo)
{
@@ -4913,8 +3346,8 @@ oscar_set_info(PurpleConnection *gc, const char *rawinfo)
oscar_set_info_and_status(account, TRUE, rawinfo, FALSE, status);
}
-static void
-oscar_set_extendedstatus(PurpleConnection *gc)
+static guint32
+oscar_get_extended_status(PurpleConnection *gc)
{
OscarData *od;
PurpleAccount *account;
@@ -4958,7 +3391,13 @@ oscar_set_extendedstatus(PurpleConnection *gc)
else if (!strcmp(status_id, OSCAR_STATUS_ID_CUSTOM))
data |= AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY;
- aim_srv_setextrainfo(od, TRUE, data, FALSE, NULL, NULL);
+ return data;
+}
+
+static void
+oscar_set_extended_status(PurpleConnection *gc)
+{
+ aim_srv_setextrainfo(purple_connection_get_protocol_data(gc), TRUE, oscar_get_extended_status(gc), FALSE, NULL, NULL);
}
static void
@@ -4999,7 +3438,7 @@ oscar_set_info_and_status(PurpleAccount *account, gboolean setinfo, const char *
else if (rawinfo != NULL)
{
char *htmlinfo = purple_strdup_withhtml(rawinfo);
- info = purple_prpl_oscar_convert_to_infotext(htmlinfo, &infolen, &info_encoding);
+ info = oscar_encode_im(htmlinfo, &infolen, NULL, &info_encoding);
g_free(htmlinfo);
if (infolen > od->rights.maxsiglen)
@@ -5032,7 +3471,7 @@ oscar_set_info_and_status(PurpleAccount *account, gboolean setinfo, const char *
/* We do this for icq too so that they work for old third party clients */
linkified = purple_markup_linkify(status_html);
- away = purple_prpl_oscar_convert_to_infotext(linkified, &awaylen, &away_encoding);
+ away = oscar_encode_im(linkified, &awaylen, NULL, &away_encoding);
g_free(linkified);
if (awaylen > od->rights.maxawaymsglen)
@@ -5061,8 +3500,6 @@ oscar_set_info_and_status(PurpleAccount *account, gboolean setinfo, const char *
const char *status_html;
status_html = purple_status_get_attr_string(status, "message");
- if (od->icq && (status_html == NULL || status_html[0] == '\0'))
- status_html = purple_status_type_get_name(status_type);
if (status_html != NULL)
{
status_text = purple_markup_strip_html(status_html);
@@ -5075,28 +3512,27 @@ oscar_set_info_and_status(PurpleAccount *account, gboolean setinfo, const char *
}
itmsurl = purple_status_get_attr_string(status, "itmsurl");
-
- /* TODO: Combine these two calls! */
- aim_srv_setextrainfo(od, FALSE, 0, TRUE, status_text, itmsurl);
- oscar_set_extendedstatus(gc);
+
+ aim_srv_setextrainfo(od, TRUE, oscar_get_extended_status(gc), TRUE, status_text, itmsurl);
g_free(status_text);
}
}
static void
-oscar_set_status_icq(PurpleAccount *account)
+oscar_set_icq_permdeny(PurpleAccount *account)
{
PurpleConnection *gc = purple_account_get_connection(account);
-
- /* Our permit/deny setting affects our invisibility */
- oscar_set_permit_deny(gc);
+ OscarData *od = purple_connection_get_protocol_data(gc);
+ gboolean invisible = purple_account_is_status_active(account, OSCAR_STATUS_ID_INVISIBLE);
/*
- * TODO: I guess we should probably wait and do this after we get
- * confirmation from the above SSI call? Right now other people
- * see our status blip to "invisible" before we appear offline.
+ * 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.
+ * 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.
*/
- oscar_set_extendedstatus(gc);
+ aim_ssi_setpermdeny(od, invisible ? PURPLE_PRIVACY_ALLOW_USERS : PURPLE_PRIVACY_DENY_USERS);
}
void
@@ -5122,21 +3558,14 @@ oscar_set_status(PurpleAccount *account, PurpleStatus *status)
return;
}
+ if (od->icq) {
+ /* Set visibility */
+ oscar_set_icq_permdeny(account);
+ }
+
/* Set the AIM-style away message for both AIM and ICQ accounts */
oscar_set_info_and_status(account, FALSE, NULL, TRUE, status);
-
- /* Set the ICQ status for ICQ accounts only */
- if (od->icq)
- oscar_set_status_icq(account);
-}
-
-#ifdef CRAZY_WARN
-void
-oscar_warn(PurpleConnection *gc, const char *name, gboolean anonymous) {
- OscarData *od = purple_connection_get_protocol_data(gc);
- aim_im_warn(od, od->conn, name, anonymous ? AIM_WARN_ANON : 0);
}
-#endif
void
oscar_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) {
@@ -5179,13 +3608,13 @@ oscar_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) {
aim_ssi_itemlist_findparentname(od->ssi.local, bname),
bname)) {
/* Not authorized -- Re-request authorization */
- purple_auth_sendrequest(gc, bname);
+ oscar_auth_sendrequest(gc, bname);
}
}
/* XXX - Should this be done from AIM accounts, as well? */
if (od->icq)
- aim_icq_getalias(od, bname);
+ aim_icq_getalias(od, bname, FALSE, NULL);
}
void oscar_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) {
@@ -5295,8 +3724,6 @@ static int purple_ssi_parseerr(OscarData *od, FlapConnection *conn, FlapFrame *f
return 1;
}
- oscar_set_status_icq(purple_connection_get_account(gc));
-
return 1;
}
@@ -5337,12 +3764,14 @@ static int purple_ssi_parselist(OscarData *od, FlapConnection *conn, FlapFrame *
PurpleAccount *account;
PurpleGroup *g;
PurpleBuddy *b;
+ GSList *cur, *next, *buddies;
struct aim_ssi_item *curitem;
guint32 tmp;
PurpleStoredImage *img;
va_list ap;
guint16 fmtver, numitems;
guint32 timestamp;
+ guint16 deny_entry_type = aim_ssi_getdenyentrytype(od);
gc = od->gc;
od = purple_connection_get_protocol_data(gc);
@@ -5355,110 +3784,109 @@ static int purple_ssi_parselist(OscarData *od, FlapConnection *conn, FlapFrame *
va_end(ap);
/* Don't attempt to re-request our buddy list later */
- if (od->getblisttimer != 0)
+ if (od->getblisttimer != 0) {
purple_timeout_remove(od->getblisttimer);
- od->getblisttimer = 0;
+ od->getblisttimer = 0;
+ }
- purple_debug_info("oscar",
- "ssi: syncing local list and server list\n");
+ purple_debug_info("oscar", "ssi: syncing local list and server list\n");
/* Clean the buddy list */
aim_ssi_cleanlist(od);
- { /* If not in server list then prune from local list */
- GSList *cur, *next;
- GSList *buddies = purple_find_buddies(account, NULL);
-
- /* Buddies */
- cur = NULL;
-
- while(buddies) {
- PurpleGroup *g;
- const char *gname;
- const char *bname;
-
- b = buddies->data;
- g = purple_buddy_get_group(b);
- gname = purple_group_get_name(g);
- bname = purple_buddy_get_name(b);
-
- 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);
-
- /* Store local alias on server */
- alias = aim_ssi_getalias(od->ssi.local, gname, bname);
- balias = purple_buddy_get_local_buddy_alias(b);
- if (!alias && balias && *balias)
- aim_ssi_aliasbuddy(od, gname, bname, balias);
- g_free(alias);
- } else {
- purple_debug_info("oscar",
- "ssi: removing buddy %s from local list\n", bname);
- /* We can't actually remove now because it will screw up our looping */
- cur = g_slist_prepend(cur, b);
- }
- buddies = g_slist_delete_link(buddies, buddies);
- }
+ /*** 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);
+ buddies;
+ buddies = g_slist_delete_link(buddies, buddies))
+ {
+ PurpleGroup *g;
+ const char *gname;
+ const char *bname;
- while (cur != NULL) {
- b = cur->data;
- cur = g_slist_remove(cur, b);
- purple_blist_remove_buddy(b);
+ b = buddies->data;
+ g = purple_buddy_get_group(b);
+ gname = purple_group_get_name(g);
+ bname = purple_buddy_get_name(b);
+
+ 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);
+
+ /* Store local alias on server */
+ alias = aim_ssi_getalias(od->ssi.local, gname, bname);
+ balias = purple_buddy_get_local_buddy_alias(b);
+ if (!alias && balias && *balias)
+ aim_ssi_aliasbuddy(od, gname, bname, balias);
+ g_free(alias);
+ } else {
+ purple_debug_info("oscar",
+ "ssi: removing buddy %s from local list\n", bname);
+ /* Queue the buddy for removal from the local list */
+ cur = g_slist_prepend(cur, b);
}
+ }
+ while (cur != NULL) {
+ purple_blist_remove_buddy(cur->data);
+ cur = g_slist_delete_link(cur, cur);
+ }
- /* Permit list */
- if (account->permit) {
- next = account->permit;
- while (next != NULL) {
- cur = next;
- next = next->next;
- 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);
- }
+ /* Permit list (ICQ doesn't have one) */
+ if (!od->icq) {
+ next = account->permit;
+ while (next != NULL) {
+ cur = next;
+ next = next->next;
+ 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);
}
}
+ }
- /* Deny list */
- if (account->deny) {
- next = account->deny;
- while (next != NULL) {
- cur = next;
- next = next->next;
- if (!aim_ssi_itemlist_finditem(od->ssi.local, NULL, cur->data, AIM_SSI_TYPE_DENY)) {
- purple_debug_info("oscar",
- "ssi: removing deny %s from local list\n", (const char *)cur->data);
- purple_privacy_deny_remove(account, cur->data, TRUE);
- }
- }
+ /* Deny list */
+ next = account->deny;
+ while (next != NULL) {
+ cur = next;
+ next = next->next;
+ 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);
}
- /* Presence settings (idle time visibility) */
- tmp = aim_ssi_getpresence(od->ssi.local);
- if (tmp != 0xFFFFFFFF) {
- const char *idle_reporting_pref;
- gboolean report_idle;
+ }
- idle_reporting_pref = purple_prefs_get_string("/purple/away/idle_reporting");
- report_idle = strcmp(idle_reporting_pref, "none") != 0;
+ /* Presence settings (idle time visibility) */
+ tmp = aim_ssi_getpresence(od->ssi.local);
+ if (tmp != 0xFFFFFFFF) {
+ const char *idle_reporting_pref;
+ gboolean report_idle;
- if (report_idle)
- aim_ssi_setpresence(od, tmp | AIM_SSI_PRESENCE_FLAG_SHOWIDLE);
- else
- aim_ssi_setpresence(od, tmp & ~AIM_SSI_PRESENCE_FLAG_SHOWIDLE);
- }
+ idle_reporting_pref = purple_prefs_get_string("/purple/away/idle_reporting");
+ report_idle = strcmp(idle_reporting_pref, "none") != 0;
+
+ if (report_idle)
+ aim_ssi_setpresence(od, tmp | AIM_SSI_PRESENCE_FLAG_SHOWIDLE);
+ else
+ aim_ssi_setpresence(od, tmp & ~AIM_SSI_PRESENCE_FLAG_SHOWIDLE);
+ }
+ /*** End code for pruning buddies from local list ***/
- } /* end pruning buddies from local list */
+ /*** Begin code for adding from server list to local list ***/
- /* Add from server list to local list */
for (curitem=od->ssi.local; curitem; curitem=curitem->next) {
- if ((curitem->name == NULL) || (g_utf8_validate(curitem->name, -1, NULL)))
+ if (curitem->name && !g_utf8_validate(curitem->name, -1, NULL))
+ /* Got node with invalid UTF-8 in the name. Skip it. */
+ break;
+
switch (curitem->type) {
case AIM_SSI_TYPE_BUDDY: { /* Buddy */
if (curitem->name) {
@@ -5467,13 +3895,7 @@ static int purple_ssi_parselist(OscarData *od, FlapConnection *conn, FlapFrame *
groupitem = aim_ssi_itemlist_find(od->ssi.local, curitem->gid, 0x0000);
gname = groupitem ? groupitem->name : NULL;
- if (gname != NULL) {
- if (g_utf8_validate(gname, -1, NULL))
- gname_utf8 = g_strdup(gname);
- else
- gname_utf8 = oscar_utf8_try_convert(account, od, gname);
- } else
- gname_utf8 = NULL;
+ gname_utf8 = oscar_utf8_try_convert(account, od, gname);
g = purple_find_group(gname_utf8 ? gname_utf8 : _("Orphans"));
if (g == NULL) {
@@ -5482,14 +3904,7 @@ static int purple_ssi_parselist(OscarData *od, FlapConnection *conn, FlapFrame *
}
alias = aim_ssi_getalias(od->ssi.local, gname, curitem->name);
- if (alias != NULL) {
- if (g_utf8_validate(alias, -1, NULL))
- alias_utf8 = g_strdup(alias);
- else
- alias_utf8 = oscar_utf8_try_convert(account, od, alias);
- g_free(alias);
- } else
- alias_utf8 = NULL;
+ alias_utf8 = oscar_utf8_try_convert(account, od, alias);
b = purple_find_buddy_in_group(account, curitem->name, g);
if (b) {
@@ -5531,13 +3946,7 @@ static int purple_ssi_parselist(OscarData *od, FlapConnection *conn, FlapFrame *
char *gname_utf8;
gname = curitem->name;
- if (gname != NULL) {
- if (g_utf8_validate(gname, -1, NULL))
- gname_utf8 = g_strdup(gname);
- else
- gname_utf8 = oscar_utf8_try_convert(account, od, gname);
- } else
- gname_utf8 = NULL;
+ gname_utf8 = oscar_utf8_try_convert(account, od, gname);
if (gname_utf8 != NULL && purple_find_group(gname_utf8) == NULL) {
g = purple_group_new(gname_utf8);
@@ -5546,12 +3955,10 @@ static int purple_ssi_parselist(OscarData *od, FlapConnection *conn, FlapFrame *
g_free(gname_utf8);
} break;
- case AIM_SSI_TYPE_PERMIT: { /* Permit buddy */
- if (curitem->name) {
- /* if (!find_permdeny_by_name(gc->permit, curitem->name)) { AAA */
- GSList *list;
- for (list=account->permit; (list && oscar_util_name_compare(curitem->name, list->data)); list=list->next);
- if (!list) {
+ 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);
+ 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);
@@ -5559,11 +3966,11 @@ static int purple_ssi_parselist(OscarData *od, FlapConnection *conn, FlapFrame *
}
} break;
+ case AIM_SSI_TYPE_ICQDENY:
case AIM_SSI_TYPE_DENY: { /* Deny buddy */
- if (curitem->name) {
- GSList *list;
- for (list=account->deny; (list && oscar_util_name_compare(curitem->name, list->data)); list=list->next);
- if (!list) {
+ if (curitem->type == deny_entry_type && curitem->name) {
+ for (cur = account->deny; (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);
@@ -5595,7 +4002,13 @@ static int purple_ssi_parselist(OscarData *od, FlapConnection *conn, FlapFrame *
} /* End of switch on curitem->type */
} /* End of for loop */
- oscar_set_status_icq(account);
+ /*** End code for adding from server list to local list ***/
+
+ if (od->icq) {
+ oscar_set_icq_permdeny(account);
+ } else {
+ oscar_set_aim_permdeny(gc);
+ }
/* Activate SSI */
/* Sending the enable causes other people to be able to see you, and you to see them */
@@ -5657,7 +4070,7 @@ static int purple_ssi_parseack(OscarData *od, FlapConnection *conn, FlapFrame *f
case 0x000e: { /* buddy requires authorization */
if ((retval->action == SNAC_SUBTYPE_FEEDBAG_ADD) && (retval->name))
- purple_auth_sendrequest(gc, retval->name);
+ oscar_auth_sendrequest(gc, retval->name);
} break;
default: { /* La la la */
@@ -5706,15 +4119,7 @@ purple_ssi_parseaddmod(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
gname_utf8 = gname ? oscar_utf8_try_convert(account, od, gname) : NULL;
alias = aim_ssi_getalias(od->ssi.local, gname, name);
- if (alias != NULL)
- {
- if (g_utf8_validate(alias, -1, NULL))
- alias_utf8 = g_strdup(alias);
- else
- alias_utf8 = oscar_utf8_try_convert(account, od, alias);
- }
- else
- alias_utf8 = NULL;
+ alias_utf8 = oscar_utf8_try_convert(account, od, alias);
g_free(alias);
b = purple_find_buddy(account, name);
@@ -5809,24 +4214,18 @@ static int purple_ssi_authgiven(OscarData *od, FlapConnection *conn, FlapFrame *
static int purple_ssi_authrequest(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
{
- PurpleConnection *gc = od->gc;
va_list ap;
const char *bn;
- const char *msg;
- PurpleAccount *account = purple_connection_get_account(gc);
- struct name_data *data;
- PurpleBuddy *buddy;
+ char *msg;
va_start(ap, fr);
bn = va_arg(ap, const char *);
- msg = va_arg(ap, const char *);
+ msg = va_arg(ap, char *);
va_end(ap);
purple_debug_info("oscar",
"ssi: received authorization request from %s\n", bn);
- buddy = purple_find_buddy(account, bn);
-
if (!msg) {
purple_debug_warning("oscar", "Received auth request from %s with "
"empty message\n", bn);
@@ -5836,16 +4235,7 @@ static int purple_ssi_authrequest(OscarData *od, FlapConnection *conn, FlapFrame
msg = NULL;
}
- data = g_new(struct name_data, 1);
- data->gc = gc;
- data->name = g_strdup(bn);
- data->nick = (buddy ? g_strdup(purple_buddy_get_alias_only(buddy)) : NULL);
-
- purple_account_request_authorization(account, bn, NULL,
- (buddy ? purple_buddy_get_alias_only(buddy) : NULL),
- msg, buddy != NULL, purple_auth_grant,
- purple_auth_dontgrant_msgprompt, data);
-
+ aim_icq_getalias(od, bn, TRUE, msg);
return 1;
}
@@ -6018,9 +4408,9 @@ int oscar_send_chat(PurpleConnection *gc, int id, const char *message, PurpleMes
PurpleConversation *conv = NULL;
struct chat_connection *c = NULL;
char *buf, *buf2, *buf3;
- guint16 charset, charsubset;
- char *charsetstr = NULL;
- int len;
+ guint16 charset;
+ char *charsetstr;
+ gsize len;
if (!(conv = purple_find_chat(gc, id)))
return -EINVAL;
@@ -6036,7 +4426,7 @@ int oscar_send_chat(PurpleConnection *gc, int id, const char *message, PurpleMes
"You cannot send IM Images in AIM chats."),
PURPLE_MESSAGE_ERROR, time(NULL));
- purple_plugin_oscar_convert_to_best_encoding(gc, NULL, buf, &buf2, &len, &charset, &charsubset);
+ buf2 = oscar_encode_im(buf, &len, &charset, &charsetstr);
/*
* Evan S. suggested that maxvis really does mean "number of
* visible characters" and not "number of bytes"
@@ -6052,10 +4442,11 @@ int oscar_send_chat(PurpleConnection *gc, int id, const char *message, PurpleMes
buf = purple_strdup_withhtml(buf3);
g_free(buf3);
- purple_plugin_oscar_convert_to_best_encoding(gc, NULL, buf, &buf2, &len, &charset, &charsubset);
+ buf2 = oscar_encode_im(buf, &len, &charset, &charsetstr);
if ((len > c->maxlen) || (len > c->maxvis)) {
- purple_debug_warning("oscar", "Could not send %s because (%i > maxlen %i) or (%i > maxvis %i)\n",
+ purple_debug_warning("oscar",
+ "Could not send %s because (%" G_GSIZE_FORMAT " > maxlen %i) or (%" G_GSIZE_FORMAT " > maxvis %i)\n",
buf2, len, c->maxlen, len, c->maxvis);
g_free(buf);
g_free(buf2);
@@ -6066,12 +4457,6 @@ int oscar_send_chat(PurpleConnection *gc, int id, const char *message, PurpleMes
message, buf2);
}
- if (charset == AIM_CHARSET_ASCII)
- charsetstr = "us-ascii";
- else if (charset == AIM_CHARSET_UNICODE)
- charsetstr = "unicode-2-0";
- else if (charset == AIM_CHARSET_LATIN_1)
- charsetstr = "iso-8859-1";
aim_chat_send_im(od, c->conn, 0, buf2, len, charsetstr, "en");
g_free(buf2);
g_free(buf);
@@ -6226,7 +4611,7 @@ char *oscar_status_text(PurpleBuddy *b)
tmp1 = purple_markup_strip_html(message);
purple_util_chrreplace(tmp1, '\n', ' ');
tmp2 = g_markup_escape_text(tmp1, -1);
- ret = purple_str_sub_away_formatters(tmp2, purple_account_get_username(account));
+ ret = oscar_util_format_string(tmp2, purple_account_get_username(account));
g_free(tmp1);
g_free(tmp2);
}
@@ -6243,125 +4628,40 @@ char *oscar_status_text(PurpleBuddy *b)
return ret;
}
-
-static int oscar_icon_req(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- PurpleConnection *gc = od->gc;
- va_list ap;
- guint16 type;
- guint8 flags = 0, length = 0;
- guchar *md5 = NULL;
-
- va_start(ap, fr);
- type = va_arg(ap, int);
-
- switch(type) {
- case 0x0000:
- case 0x0001: {
- flags = va_arg(ap, int);
- length = va_arg(ap, int);
- md5 = va_arg(ap, guchar *);
-
- if ((flags == 0x00) || (flags == 0x41)) {
- if (!flap_connection_getbytype(od, SNAC_FAMILY_BART) && !od->iconconnecting) {
- od->iconconnecting = TRUE;
- od->set_icon = TRUE;
- aim_srv_requestnew(od, SNAC_FAMILY_BART);
- } else {
- PurpleAccount *account = purple_connection_get_account(gc);
- PurpleStoredImage *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);
- }
- }
- } else if (flags == 0x81) {
- PurpleAccount *account = purple_connection_get_account(gc);
- PurpleStoredImage *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);
- }
- }
- } break;
-
- case 0x0002: { /* We just set an "available" message? */
- } break;
- }
-
- va_end(ap);
-
- return 0;
-}
-
-void oscar_set_permit_deny(PurpleConnection *gc) {
+void oscar_set_aim_permdeny(PurpleConnection *gc) {
PurpleAccount *account = purple_connection_get_account(gc);
OscarData *od = purple_connection_get_protocol_data(gc);
- PurplePrivacyType perm_deny;
/*
- * For ICQ the permit/deny setting controls who you can see you
- * online when you set your status to "invisible." If we're ICQ
- * and we're invisible then we need to use one of
- * PURPLE_PRIVACY_ALLOW_USERS or PURPLE_PRIVACY_ALLOW_BUDDYLIST or
- * PURPLE_PRIVACY_DENY_USERS if we actually want to be invisible
- * to anyone.
- *
- * These three permit/deny settings correspond to:
- * 1. Invisible to everyone except the people on my "permit" list
- * 2. Invisible to everyone except the people on my buddy list
- * 3. Invisible only to the people on my "deny" list
- *
- * It would be nice to allow cases 2 and 3, but our UI doesn't have
- * a nice way to do it. For now we just force case 1.
+ * Conveniently there is a one-to-one mapping between the
+ * values of libpurple's PurplePrivacyType and the values used
+ * by the oscar protocol.
*/
- if (od->icq && purple_account_is_status_active(account, OSCAR_STATUS_ID_INVISIBLE))
- perm_deny = PURPLE_PRIVACY_ALLOW_USERS;
- else
- perm_deny = account->perm_deny;
-
- if (od->ssi.received_data)
- /*
- * Conveniently there is a one-to-one mapping between the
- * values of libpurple's PurplePrivacyType and the values used
- * by the oscar protocol.
- */
- aim_ssi_setpermdeny(od, perm_deny, 0xffffffff);
+ aim_ssi_setpermdeny(od, account->perm_deny);
}
void oscar_add_permit(PurpleConnection *gc, const char *who) {
OscarData *od = purple_connection_get_protocol_data(gc);
purple_debug_info("oscar", "ssi: About to add a permit\n");
- if (od->ssi.received_data)
- aim_ssi_addpermit(od, who);
+ aim_ssi_add_to_private_list(od, who, AIM_SSI_TYPE_PERMIT);
}
void oscar_add_deny(PurpleConnection *gc, const char *who) {
OscarData *od = purple_connection_get_protocol_data(gc);
purple_debug_info("oscar", "ssi: About to add a deny\n");
- if (od->ssi.received_data)
- aim_ssi_adddeny(od, who);
+ aim_ssi_add_to_private_list(od, who, aim_ssi_getdenyentrytype(od));
}
void oscar_rem_permit(PurpleConnection *gc, const char *who) {
OscarData *od = purple_connection_get_protocol_data(gc);
purple_debug_info("oscar", "ssi: About to delete a permit\n");
- if (od->ssi.received_data)
- aim_ssi_delpermit(od, who);
+ aim_ssi_del_from_private_list(od, who, AIM_SSI_TYPE_PERMIT);
}
void oscar_rem_deny(PurpleConnection *gc, const char *who) {
OscarData *od = purple_connection_get_protocol_data(gc);
purple_debug_info("oscar", "ssi: About to delete a deny\n");
- if (od->ssi.received_data)
- aim_ssi_deldeny(od, who);
+ aim_ssi_del_from_private_list(od, who, aim_ssi_getdenyentrytype(od));
}
GList *
@@ -6653,7 +4953,7 @@ oscar_close_directim(gpointer object, gpointer ignored)
peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
/* OSCAR_DISCONNECT_LOCAL_CLOSED doesn't write anything to the convo
- * window. Let the user know that we canceled the Direct IM. */
+ * 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));
@@ -6693,7 +4993,6 @@ oscar_get_aim_info_cb(PurpleBlistNode *node, gpointer ignore)
static GList *
oscar_buddy_menu(PurpleBuddy *buddy) {
-
PurpleConnection *gc;
OscarData *od;
GList *menu;
@@ -6731,6 +5030,7 @@ oscar_buddy_menu(PurpleBuddy *buddy) {
PURPLE_CALLBACK(oscar_get_icqxstatusmsg),
NULL, NULL);
menu = g_list_prepend(menu, act);
+ menu = g_list_prepend(menu, create_visibility_menu_item(od, bname));
}
if (userinfo &&
@@ -6756,15 +5056,6 @@ oscar_buddy_menu(PurpleBuddy *buddy) {
}
menu = g_list_prepend(menu, act);
}
-#if 0
- /* TODO: This menu item should be added by the core */
- if (userinfo->capabilities & OSCAR_CAPABILITY_GETFILE) {
- act = purple_menu_action_new(_("Get File"),
- PURPLE_CALLBACK(oscar_ask_getfile),
- NULL, NULL);
- menu = g_list_prepend(menu, act);
- }
-#endif
}
if (od->ssi.received_data && purple_buddy_get_group(buddy) != NULL)
@@ -6778,7 +5069,7 @@ oscar_buddy_menu(PurpleBuddy *buddy) {
if (gname && aim_ssi_waitingforauth(od->ssi.local, gname, bname))
{
act = purple_menu_action_new(_("Re-request Authorization"),
- PURPLE_CALLBACK(purple_auth_sendrequest_menu),
+ PURPLE_CALLBACK(oscar_auth_sendrequest_menu),
NULL, NULL);
menu = g_list_prepend(menu, act);
}
@@ -6815,7 +5106,7 @@ oscar_icq_privacy_opts(PurpleConnection *gc, PurpleRequestFields *fields)
purple_account_set_bool(account, "authorization", auth);
purple_account_set_bool(account, "web_aware", web_aware);
- oscar_set_extendedstatus(gc);
+ oscar_set_extended_status(gc);
aim_icq_setsecurity(od, auth, web_aware);
}
@@ -6930,41 +5221,29 @@ static void oscar_show_awaitingauth(PurplePluginAction *action)
{
PurpleConnection *gc = (PurpleConnection *) action->context;
OscarData *od = purple_connection_get_protocol_data(gc);
- gchar *text, *tmp;
- GSList *buddies;
- PurpleAccount *account;
- int num=0;
-
- text = g_strdup("");
- account = purple_connection_get_account(gc);
+ PurpleAccount *account = purple_connection_get_account(gc);
+ GSList *buddies, *filtered_buddies, *cur;
+ gchar *text;
buddies = purple_find_buddies(account, NULL);
- while (buddies) {
+ filtered_buddies = NULL;
+ for (cur = buddies; cur != NULL; cur = cur->next) {
PurpleBuddy *buddy;
const gchar *bname, *gname;
- buddy = buddies->data;
+ 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)) {
- const gchar *alias = purple_buddy_get_alias_only(buddy);
- if (alias)
- tmp = g_strdup_printf("%s %s (%s)<br>", text, bname, alias);
- else
- tmp = g_strdup_printf("%s %s<br>", text, bname);
- g_free(text);
- text = tmp;
-
- num++;
+ filtered_buddies = g_slist_prepend(filtered_buddies, buddy);
}
-
- buddies = g_slist_delete_link(buddies, buddies);
}
- if (!num) {
- g_free(text);
- text = g_strdup(_("<i>you are not waiting for authorization</i>"));
- }
+ g_slist_free(buddies);
+
+ filtered_buddies = g_slist_reverse(filtered_buddies);
+ text = oscar_format_buddies(filtered_buddies, _("you are not waiting for authorization"));
+ g_slist_free(filtered_buddies);
purple_notify_formatted(gc, NULL, _("You are awaiting authorization from "
"the following buddies"), _("You can re-request "
@@ -7179,6 +5458,12 @@ oscar_actions(PurplePlugin *plugin, gpointer context)
act = purple_plugin_action_new(_("Set Privacy Options..."),
oscar_show_icq_privacy_opts);
menu = g_list_prepend(menu, act);
+
+ act = purple_plugin_action_new("Show Visible List", oscar_show_visible_list);
+ menu = g_list_prepend(menu, act);
+
+ act = purple_plugin_action_new("Show Invisible List", oscar_show_invisible_list);
+ menu = g_list_prepend(menu, act);
}
else
{
@@ -7208,12 +5493,6 @@ oscar_actions(PurplePlugin *plugin, gpointer context)
oscar_show_find_email);
menu = g_list_prepend(menu, act);
-#if 0
- act = purple_plugin_action_new(_("Search for Buddy by Information"),
- show_find_info);
- menu = g_list_prepend(menu, act);
-#endif
-
menu = g_list_reverse(menu);
return menu;
diff --git a/libpurple/protocols/oscar/oscar.h b/libpurple/protocols/oscar/oscar.h
index a20d09faea..638536e793 100644
--- a/libpurple/protocols/oscar/oscar.h
+++ b/libpurple/protocols/oscar/oscar.h
@@ -103,16 +103,6 @@ extern "C" {
#define AIM_ICONIDENT "AVT1picture.id"
/*
- * Current Maximum Length for Chat Room Messages
- *
- * This is actually defined by the protocol to be
- * dynamic, but I have yet to see due cause to
- * define it dynamically here. Maybe later.
- *
- */
-#define MAXCHATMSGLEN 512
-
-/*
* Found by trial and error.
*/
#define MAXAVAILMSGLEN 251
@@ -143,167 +133,6 @@ struct _ClientInfo
const char *lang; /* two-letter abbrev */
};
-/* Needs to be checked */
-#define CLIENTINFO_AIM_3_5_1670 { \
- "AOL Instant Messenger (SM), version 3.5.1670/WIN32", \
- 0x0004, \
- 0x0003, 0x0005, \
- 0x0000, 0x0686, \
- 0x0000002a, \
- "us", "en", \
-}
-
-/* Needs to be checked */
-/* Latest winaim without ssi */
-#define CLIENTINFO_AIM_4_1_2010 { \
- "AOL Instant Messenger (SM), version 4.1.2010/WIN32", \
- 0x0004, \
- 0x0004, 0x0001, \
- 0x0000, 0x07da, \
- 0x0000004b, \
- "us", "en", \
-}
-
-/* Needs to be checked */
-#define CLIENTINFO_AIM_4_3_2188 { \
- "AOL Instant Messenger (SM), version 4.3.2188/WIN32", \
- 0x0109, \
- 0x0400, 0x0003, \
- 0x0000, 0x088c, \
- 0x00000086, \
- "us", "en", \
-}
-
-/* Needs to be checked */
-#define CLIENTINFO_AIM_4_8_2540 { \
- "AOL Instant Messenger (SM), version 4.8.2540/WIN32", \
- 0x0109, \
- 0x0004, 0x0008, \
- 0x0000, 0x09ec, \
- 0x000000af, \
- "us", "en", \
-}
-
-/* Needs to be checked */
-#define CLIENTINFO_AIM_5_0_2938 { \
- "AOL Instant Messenger, version 5.0.2938/WIN32", \
- 0x0109, \
- 0x0005, 0x0000, \
- 0x0000, 0x0b7a, \
- 0x00000000, \
- "us", "en", \
-}
-
-#define CLIENTINFO_AIM_5_1_3036 { \
- "AOL Instant Messenger, version 5.1.3036/WIN32", \
- 0x0109, \
- 0x0005, 0x0001, \
- 0x0000, 0x0bdc, \
- 0x000000d2, \
- "us", "en", \
-}
-
-#define CLIENTINFO_AIM_5_5_3415 { \
- "AOL Instant Messenger, version 5.5.3415/WIN32", \
- 0x0109, \
- 0x0005, 0x0005, \
- 0x0000, 0x0057, \
- 0x000000ef, \
- "us", "en", \
-}
-
-#define CLIENTINFO_AIM_5_9_3702 { \
- "AOL Instant Messenger, version 5.9.3702/WIN32", \
- 0x0109, \
- 0x0005, 0x0009, \
- 0x0000, 0x0e76, \
- 0x00000111, \
- "us", "en", \
-}
-
-#define CLIENTINFO_ICHAT_1_0 { \
- "Apple iChat", \
- 0x311a, \
- 0x0001, 0x0000, \
- 0x0000, 0x003c, \
- 0x000000c6, \
- "us", "en", \
-}
-
-/* Needs to be checked */
-#define CLIENTINFO_ICQ_4_65_3281 { \
- "ICQ Inc. - Product of ICQ (TM) 2000b.4.65.1.3281.85", \
- 0x010a, \
- 0x0004, 0x0041, \
- 0x0001, 0x0cd1, \
- 0x00000055, \
- "us", "en", \
-}
-
-/* Needs to be checked */
-#define CLIENTINFO_ICQ_5_34_3728 { \
- "ICQ Inc. - Product of ICQ (TM).2002a.5.34.1.3728.85", \
- 0x010a, \
- 0x0005, 0x0022, \
- 0x0001, 0x0e8f, \
- 0x00000055, \
- "us", "en", \
-}
-
-#define CLIENTINFO_ICQ_5_45_3777 { \
- "ICQ Inc. - Product of ICQ (TM).2003a.5.45.1.3777.85", \
- 0x010a, \
- 0x0005, 0x002d, \
- 0x0001, 0x0ec1, \
- 0x00000055, \
- "us", "en", \
-}
-
-#define CLIENTINFO_ICQ6_6_0_6059 { \
- "ICQ Client", \
- 0x010a, \
- 0x0006, 0x0000, \
- 0x0000, 0x17ab, \
- 0x00007535, \
- "us", "en", \
-}
-
-#define CLIENTINFO_ICQBASIC_14_3_1068 { \
- "ICQBasic", \
- 0x010a, \
- 0x0014, 0x0003, \
- 0x0000, 0x042c, \
- 0x0000043d, \
- "us", "en", \
-}
-
-#define CLIENTINFO_ICQBASIC_14_34_3000 { \
- "ICQBasic", \
- 0x010a, \
- 0x0014, 0x0034, \
- 0x0000, 0x0bb8, \
- 0x0000043d, \
- "us", "en", \
-}
-
-#define CLIENTINFO_ICQBASIC_14_34_3096 { \
- "ICQBasic", \
- 0x010a, \
- 0x0014, 0x0034, \
- 0x0000, 0x0c18, \
- 0x0000043d, \
- "us", "en", \
-}
-
-#define CLIENTINFO_NETSCAPE_7_0_1 { \
- "Netscape 2000 an approved user of AOL Instant Messenger (SM)", \
- 0x1d0d, \
- 0x0007, 0x0000, \
- 0x0001, 0x0000, \
- 0x00000058, \
- "us", "en", \
-}
-
/*
* We need to use the major-minor-micro versions from the official
* AIM and ICQ programs here or AOL won't let us use certain features.
@@ -329,9 +158,6 @@ struct _ClientInfo
"us", "en", \
}
-#define CLIENTINFO_AIM_KNOWNGOOD CLIENTINFO_AIM_5_1_3036
-#define CLIENTINFO_ICQ_KNOWNGOOD CLIENTINFO_ICQBASIC_14_34_3096
-
typedef enum
{
OSCAR_DISCONNECT_DONE, /* not considered an error */
@@ -376,7 +202,24 @@ typedef enum
#define OSCAR_CAPABILITY_NEWCAPS 0x0000000020000000LL
#define OSCAR_CAPABILITY_XTRAZ 0x0000000040000000LL
#define OSCAR_CAPABILITY_GENERICUNKNOWN 0x0000000080000000LL
-#define OSCAR_CAPABILITY_LAST 0x0000000100000000LL
+#define OSCAR_CAPABILITY_HTML_MSGS 0x0000000100000000LL
+#define OSCAR_CAPABILITY_LAST 0x0000000200000000LL
+
+#define OSCAR_STATUS_ID_INVISIBLE "invisible"
+#define OSCAR_STATUS_ID_OFFLINE "offline"
+#define OSCAR_STATUS_ID_AVAILABLE "available"
+#define OSCAR_STATUS_ID_AWAY "away"
+#define OSCAR_STATUS_ID_DND "dnd"
+#define OSCAR_STATUS_ID_NA "na"
+#define OSCAR_STATUS_ID_OCCUPIED "occupied"
+#define OSCAR_STATUS_ID_FREE4CHAT "free4chat"
+#define OSCAR_STATUS_ID_CUSTOM "custom"
+#define OSCAR_STATUS_ID_MOBILE "mobile"
+#define OSCAR_STATUS_ID_EVIL "evil"
+#define OSCAR_STATUS_ID_DEPRESSION "depression"
+#define OSCAR_STATUS_ID_ATHOME "athome"
+#define OSCAR_STATUS_ID_ATWORK "atwork"
+#define OSCAR_STATUS_ID_LUNCH "lunch"
/*
* Byte Stream type. Sort of.
@@ -395,8 +238,8 @@ typedef enum
struct _ByteStream
{
guint8 *data;
- guint32 len;
- guint32 offset;
+ size_t len;
+ size_t offset;
};
struct _QueuedSnac
@@ -526,7 +369,7 @@ struct _OscarData
*/
IcbmCookie *msgcookies;
- struct aim_icq_info *icq_info;
+ GSList *icq_info;
/** Only used when connecting with the old-style BUCP login. */
struct aim_authresp_info *authinfo;
@@ -580,10 +423,8 @@ struct _OscarData
#define AIM_ICQ_STATE_WEBAWARE 0x00010000
#define AIM_ICQ_STATE_HIDEIP 0x00020000
#define AIM_ICQ_STATE_BIRTHDAY 0x00080000
-#define AIM_ICQ_STATE_DIRECTDISABLED 0x00100000
#define AIM_ICQ_STATE_ICQHOMEPAGE 0x00200000
#define AIM_ICQ_STATE_DIRECTREQUIREAUTH 0x10000000
-#define AIM_ICQ_STATE_DIRECTCONTACTLIST 0x20000000
/**
* Only used when connecting with the old-style BUCP login.
@@ -669,8 +510,8 @@ void flap_connection_send(FlapConnection *conn, FlapFrame *frame);
void flap_connection_send_version(OscarData *od, FlapConnection *conn);
void flap_connection_send_version_with_cookie(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy);
void flap_connection_send_version_with_cookie_and_clientinfo(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy, ClientInfo *ci, gboolean allow_multiple_login);
-void flap_connection_send_snac(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, guint16 flags, aim_snacid_t snacid, ByteStream *data);
-void flap_connection_send_snac_with_priority(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, guint16 flags, aim_snacid_t snacid, ByteStream *data, gboolean high_priority);
+void flap_connection_send_snac(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, aim_snacid_t snacid, ByteStream *data);
+void flap_connection_send_snac_with_priority(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, aim_snacid_t snacid, ByteStream *data, gboolean high_priority);
void flap_connection_send_keepalive(OscarData *od, FlapConnection *conn);
FlapFrame *flap_frame_new(OscarData *od, guint16 channel, int datalen);
@@ -682,64 +523,26 @@ void oscar_data_destroy(OscarData *);
void oscar_data_addhandler(OscarData *od, guint16 family, guint16 subtype, aim_rxcallback_t newhandler, guint16 flags);
aim_rxcallback_t aim_callhandler(OscarData *od, guint16 family, guint16 subtype);
-/* misc.c */
-#define AIM_VISIBILITYCHANGE_PERMITADD 0x05
-#define AIM_VISIBILITYCHANGE_PERMITREMOVE 0x06
-#define AIM_VISIBILITYCHANGE_DENYADD 0x07
-#define AIM_VISIBILITYCHANGE_DENYREMOVE 0x08
-
-#define AIM_PRIVFLAGS_ALLOWIDLE 0x01
-#define AIM_PRIVFLAGS_ALLOWMEMBERSINCE 0x02
-
-#define AIM_WARN_ANON 0x01
-
-
-
/* 0x0001 - family_oservice.c */
/* 0x0002 */ void aim_srv_clientready(OscarData *od, FlapConnection *conn);
/* 0x0004 */ void aim_srv_requestnew(OscarData *od, guint16 serviceid);
/* 0x0006 */ void aim_srv_reqrates(OscarData *od, FlapConnection *conn);
/* 0x0008 */ void aim_srv_rates_addparam(OscarData *od, FlapConnection *conn);
-/* 0x0009 */ void aim_srv_rates_delparam(OscarData *od, FlapConnection *conn);
-/* 0x000c */ void aim_srv_sendpauseack(OscarData *od, FlapConnection *conn);
/* 0x000e */ void aim_srv_reqpersonalinfo(OscarData *od, FlapConnection *conn);
/* 0x0011 */ void aim_srv_setidle(OscarData *od, guint32 idletime);
-/* 0x0014 */ void aim_srv_setprivacyflags(OscarData *od, FlapConnection *conn, guint32);
-/* 0x0016 */ void aim_srv_nop(OscarData *od, FlapConnection *conn);
/* 0x0017 */ void aim_srv_setversions(OscarData *od, FlapConnection *conn);
/* 0x001e */ int aim_srv_setextrainfo(OscarData *od, gboolean seticqstatus, guint32 icqstatus, gboolean setstatusmsg, const char *statusmsg, const char *itmsurl);
+void aim_srv_set_dc_info(OscarData *od);
void aim_bos_reqrights(OscarData *od, FlapConnection *conn);
-int aim_bos_changevisibility(OscarData *od, FlapConnection *conn, int, const char *);
-void aim_bos_setgroupperm(OscarData *od, FlapConnection *conn, guint32 mask);
-
-
-
-#define AIM_CLIENTTYPE_UNKNOWN 0x0000
-#define AIM_CLIENTTYPE_MC 0x0001
-#define AIM_CLIENTTYPE_WINAIM 0x0002
-#define AIM_CLIENTTYPE_WINAIM41 0x0003
-#define AIM_CLIENTTYPE_AOL_TOC 0x0004
-guint16 aim_im_fingerprint(const guint8 *msghdr, int len);
-#define AIM_RATE_CODE_CHANGE 0x0001
-#define AIM_RATE_CODE_WARNING 0x0002
#define AIM_RATE_CODE_LIMIT 0x0003
-#define AIM_RATE_CODE_CLEARLIMIT 0x0004
-void aim_ads_requestads(OscarData *od, FlapConnection *conn);
-
-
/* family_icbm.c */
-#define AIM_OFT_SUBTYPE_SEND_FILE 0x0001
#define AIM_OFT_SUBTYPE_SEND_DIR 0x0002
-#define AIM_OFT_SUBTYPE_GET_FILE 0x0011
-#define AIM_OFT_SUBTYPE_GET_LIST 0x0012
-#define AIM_TRANSFER_DENY_NOTSUPPORTED 0x0000
#define AIM_TRANSFER_DENY_DECLINE 0x0001
-#define AIM_TRANSFER_DENY_NOTACCEPTING 0x0002
#define AIM_IMPARAM_FLAG_CHANNEL_MSGS_ALLOWED 0x00000001
#define AIM_IMPARAM_FLAG_MISSED_CALLS_ENABLED 0x00000002
@@ -747,25 +550,19 @@ void aim_ads_requestads(OscarData *od, FlapConnection *conn);
#define AIM_IMPARAM_FLAG_SMS_SUPPORTED 0x00000010
#define AIM_IMPARAM_FLAG_OFFLINE_MSGS_ALLOWED 0x00000100
-/* This is what the server will give you if you don't set them yourself. */
-/* This is probably out of date. */
-#define AIM_IMPARAM_DEFAULTS { \
- 0, \
- AIM_IMPARAM_FLAG_CHANNEL_MSGS_ALLOWED | AIM_IMPARAM_FLAG_MISSED_CALLS_ENABLED, \
- 512, /* !! Note how small this is. */ \
- (99.9)*10, (99.9)*10, \
- 1000 /* !! And how large this is. */ \
-}
-
-/* This is what most AIM versions use. */
-/* This is probably out of date. */
-#define AIM_IMPARAM_REASONABLE { \
- 0, \
- AIM_IMPARAM_FLAG_CHANNEL_MSGS_ALLOWED | AIM_IMPARAM_FLAG_MISSED_CALLS_ENABLED, \
- 8000, \
- (99.9)*10, (99.9)*10, \
- 0 \
-}
+/**
+ * This flag tells the server that we always send HTML in messages
+ * sent from an ICQ account to an ICQ account. (If this flag is
+ * not sent then plaintext is sent ICQ<-->ICQ (HTML is sent in all
+ * other cases)).
+ *
+ * If we send an HTML message to an old client that doesn't support
+ * HTML messages, then the oscar servers will merrily strip the HTML
+ * for us.
+ *
+ * All incoming IMs are treated as HTML.
+ */
+#define AIM_IMPARAM_FLAG_USE_HTML_FOR_ICQ 0x00000400
struct aim_icbmparameters
{
@@ -813,9 +610,6 @@ void oscar_chat_destroy(struct chat_connection *cc);
#define AIM_IMFLAGS_HASICON 0x0020 /* already has icon */
#define AIM_IMFLAGS_SUBENC_MACINTOSH 0x0040 /* damn that Steve Jobs! */
#define AIM_IMFLAGS_CUSTOMFEATURES 0x0080 /* features field present */
-#define AIM_IMFLAGS_EXTDATA 0x0100
-#define AIM_IMFLAGS_X 0x0200
-#define AIM_IMFLAGS_MULTIPART 0x0400 /* ->mpmsg section valid */
#define AIM_IMFLAGS_OFFLINE 0x0800 /* send to offline user */
#define AIM_IMFLAGS_TYPINGNOT 0x1000 /* typing notification */
@@ -824,30 +618,6 @@ void oscar_chat_destroy(struct chat_connection *cc);
#define AIM_CHARSET_LATIN_1 0x0003 /* ISO 8859-1 */
/*
- * Multipart message structures.
- */
-typedef struct aim_mpmsg_section_s
-{
- guint16 charset;
- guint16 charsubset;
- gchar *data;
- guint16 datalen;
- struct aim_mpmsg_section_s *next;
-} aim_mpmsg_section_t;
-
-typedef struct aim_mpmsg_s
-{
- unsigned int numparts;
- aim_mpmsg_section_t *parts;
-} aim_mpmsg_t;
-
-int aim_mpmsg_init(OscarData *od, aim_mpmsg_t *mpm);
-int aim_mpmsg_addraw(OscarData *od, aim_mpmsg_t *mpm, guint16 charset, guint16 charsubset, const gchar *data, guint16 datalen);
-int aim_mpmsg_addascii(OscarData *od, aim_mpmsg_t *mpm, const char *ascii);
-int aim_mpmsg_addunicode(OscarData *od, aim_mpmsg_t *mpm, const guint16 *unicode, guint16 unicodelen);
-void aim_mpmsg_free(OscarData *od, aim_mpmsg_t *mpm);
-
-/*
* Arguments to aim_send_im_ext().
*
* This is really complicated. But immensely versatile.
@@ -855,84 +625,39 @@ void aim_mpmsg_free(OscarData *od, aim_mpmsg_t *mpm);
*/
struct aim_sendimext_args
{
-
/* These are _required_ */
const char *destbn;
guint32 flags; /* often 0 */
- /* Only required if not using multipart messages */
const char *msg;
- int msglen;
-
- /* Required if ->msg is not provided */
- aim_mpmsg_t *mpmsg;
+ gsize msglen;
/* Only used if AIM_IMFLAGS_HASICON is set */
guint32 iconlen;
time_t iconstamp;
guint32 iconsum;
- /* Only used if AIM_IMFLAGS_CUSTOMFEATURES is set */
guint16 featureslen;
guint8 *features;
- /* Only used if AIM_IMFLAGS_CUSTOMCHARSET is set and mpmsg not used */
guint16 charset;
- guint16 charsubset;
-};
-
-/*
- * Arguments to aim_send_rtfmsg().
- */
-struct aim_sendrtfmsg_args
-{
- const char *destbn;
- guint32 fgcolor;
- guint32 bgcolor;
- const char *rtfmsg; /* must be in RTF */
};
/*
* This information is provided in the Incoming ICBM callback for
* Channel 1 ICBM's.
- *
- * Note that although CUSTOMFEATURES and CUSTOMCHARSET say they
- * are optional, both are always set by the current libfaim code.
- * That may or may not change in the future. It is mainly for
- * consistency with aim_sendimext_args.
- *
- * Multipart messages require some explanation. If you want to use them,
- * I suggest you read all the comments in family_icbm.c.
- *
*/
struct aim_incomingim_ch1_args
{
-
- /* Always provided */
- aim_mpmsg_t mpmsg;
guint32 icbmflags; /* some flags apply only to ->msg, not all mpmsg */
time_t timestamp; /* Only set for offline messages */
- /* Only provided if message has a human-readable section */
gchar *msg;
- int msglen;
/* Only provided if AIM_IMFLAGS_HASICON is set */
time_t iconstamp;
guint32 iconlen;
guint16 iconsum;
-
- /* Only provided if AIM_IMFLAGS_CUSTOMFEATURES is set */
- guint8 *features;
- guint8 featureslen;
-
- /* Only provided if AIM_IMFLAGS_EXTDATA is set */
- guint8 extdatalen;
- guint8 *extdata;
-
- /* Only used if AIM_IMFLAGS_CUSTOMCHARSET is set */
- guint16 charset;
- guint16 charsubset;
};
/* Valid values for channel 2 args->status */
@@ -967,10 +692,8 @@ struct _IcbmArgsCh2
struct aim_chat_roominfo roominfo;
} chat;
struct {
- guint16 msgtype;
- guint32 fgcolor;
- guint32 bgcolor;
- const char *rtfmsg;
+ guint8 msgtype;
+ const char *msg;
} rtfmsg;
struct {
guint16 subtype;
@@ -982,11 +705,6 @@ struct _IcbmArgsCh2
void *destructor; /* used internally only */
};
-/* Valid values for channel 4 args->type */
-#define AIM_ICQMSG_AUTHREQUEST 0x0006
-#define AIM_ICQMSG_AUTHDENIED 0x0007
-#define AIM_ICQMSG_AUTHGRANTED 0x0008
-
struct aim_incomingim_ch4_args
{
guint32 uin; /* Of the sender of the ICBM */
@@ -1003,7 +721,6 @@ struct aim_incomingim_ch4_args
/* 0x0006 */ int aim_im_sendch1(OscarData *, const char *destbn, guint16 flags, const char *msg);
/* 0x0006 */ int aim_im_sendch2_chatinvite(OscarData *od, const char *bn, const char *msg, guint16 exchange, const char *roomname, guint16 instance);
/* 0x0006 */ int aim_im_sendch2_icon(OscarData *od, const char *bn, const guint8 *icon, int iconlen, time_t stamp, guint16 iconsum);
-/* 0x0006 */ int aim_im_sendch2_rtfmsg(OscarData *od, struct aim_sendrtfmsg_args *args);
/* 0x0006 */ void aim_im_sendch2_cancel(PeerConnection *peer_conn);
/* 0x0006 */ void aim_im_sendch2_connected(PeerConnection *peer_conn);
@@ -1012,38 +729,23 @@ struct aim_incomingim_ch4_args
/* 0x0006 */ void aim_im_sendch2_sendfile_requestdirect(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 port, guint16 requestnumber, const gchar *filename, guint32 size, guint16 numfiles);
/* 0x0006 */ void aim_im_sendch2_sendfile_requestproxy(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 pin, guint16 requestnumber, const gchar *filename, guint32 size, guint16 numfiles);
-/* 0x0006 */ int aim_im_sendch2_geticqaway(OscarData *od, const char *bn, int type);
-/* 0x0006 */ int aim_im_sendch4(OscarData *od, const char *bn, guint16 type, const char *message);
-/* 0x0008 */ int aim_im_warn(OscarData *od, FlapConnection *conn, const char *destbn, guint32 flags);
/* 0x000b */ int aim_im_denytransfer(OscarData *od, const char *bn, const guchar *cookie, guint16 code);
/* 0x0010 */ int aim_im_reqofflinemsgs(OscarData *od);
/* 0x0014 */ int aim_im_sendmtn(OscarData *od, guint16 type1, const char *bn, guint16 type2);
/* 0x000b */ int icq_relay_xstatus (OscarData *od, const char *sn, const guchar* cookie);
void aim_icbm_makecookie(guchar* cookie);
-gchar *oscar_encoding_extract(const char *encoding);
-gchar *oscar_encoding_to_utf8(PurpleAccount *account, const char *encoding, const char *text, int textlen);
-gchar *purple_plugin_oscar_decode_im_part(PurpleAccount *account, const char *sourcebn, guint16 charset, guint16 charsubset, const gchar *data, gsize datalen);
-
+void aim_im_send_icq_confirmation(OscarData *od, const char *bn, const guchar *cookie);
/* 0x0002 - family_locate.c */
/*
* AIM User Info, Standard Form.
*/
-#define AIM_FLAG_UNCONFIRMED 0x0001 /* "damned transients" */
#define AIM_FLAG_ADMINISTRATOR 0x0002
#define AIM_FLAG_AOL 0x0004
-#define AIM_FLAG_OSCAR_PAY 0x0008
-#define AIM_FLAG_FREE 0x0010
#define AIM_FLAG_AWAY 0x0020
-#define AIM_FLAG_ICQ 0x0040
#define AIM_FLAG_WIRELESS 0x0080
-#define AIM_FLAG_UNKNOWN100 0x0100
-#define AIM_FLAG_IMFORWARDING 0x0200
+#define AIM_FLAG_ICQ 0x0040
#define AIM_FLAG_ACTIVEBUDDY 0x0400
-#define AIM_FLAG_UNKNOWN800 0x0800
-#define AIM_FLAG_ONEWAYWIRELESS 0x1000
-#define AIM_FLAG_NOKNOCKKNOCK 0x00040000
-#define AIM_FLAG_FORWARD_MOBILE 0x00080000
#define AIM_USERINFO_PRESENT_FLAGS 0x00000001
#define AIM_USERINFO_PRESENT_MEMBERSINCE 0x00000002
@@ -1116,22 +818,8 @@ struct aim_invite_priv
guint16 instance;
};
-#define AIM_COOKIETYPE_UNKNOWN 0x00
-#define AIM_COOKIETYPE_ICBM 0x01
-#define AIM_COOKIETYPE_ADS 0x02
-#define AIM_COOKIETYPE_BOS 0x03
-#define AIM_COOKIETYPE_IM 0x04
-#define AIM_COOKIETYPE_CHAT 0x05
-#define AIM_COOKIETYPE_CHATNAV 0x06
-#define AIM_COOKIETYPE_INVITE 0x07
-/* we'll move OFT up a bit to give breathing room. not like it really
- * matters. */
-#define AIM_COOKIETYPE_OFTIM 0x10
-#define AIM_COOKIETYPE_OFTGET 0x11
-#define AIM_COOKIETYPE_OFTSEND 0x12
-#define AIM_COOKIETYPE_OFTVOICE 0x13
-#define AIM_COOKIETYPE_OFTIMAGE 0x14
-#define AIM_COOKIETYPE_OFTICON 0x15
+#define AIM_COOKIETYPE_CHAT 0x01
+#define AIM_COOKIETYPE_INVITE 0x02
aim_userinfo_t *aim_locate_finduserinfo(OscarData *od, const char *bn);
void aim_locate_dorequest(OscarData *od);
@@ -1139,10 +827,6 @@ void aim_locate_dorequest(OscarData *od);
/* 0x0002 */ int aim_locate_reqrights(OscarData *od);
/* 0x0004 */ int aim_locate_setcaps(OscarData *od, guint64 caps);
/* 0x0004 */ int aim_locate_setprofile(OscarData *od, const char *profile_encoding, const gchar *profile, const int profile_len, const char *awaymsg_encoding, const gchar *awaymsg, const int awaymsg_len);
-/* 0x0005 */ int aim_locate_getinfo(OscarData *od, const char *, guint16);
-/* 0x0009 */ int aim_locate_setdirinfo(OscarData *od, const char *first, const char *middle, const char *last, const char *maiden, const char *nickname, const char *street, const char *city, const char *state, const char *zip, int country, guint16 privacy);
-/* 0x000b */ int aim_locate_000b(OscarData *od, const char *bn);
-/* 0x000f */ int aim_locate_setinterests(OscarData *od, const char *interest1, const char *interest2, const char *interest3, const char *interest4, const char *interest5, guint16 privacy);
/* 0x0015 */ int aim_locate_getinfoshort(OscarData *od, const char *bn, guint32 flags);
guint64 aim_locate_getcaps(OscarData *od, ByteStream *bs, int len);
@@ -1157,25 +841,11 @@ int icq_im_xstatus_request(OscarData *od, const char *sn);
/* 0x0003 - family_buddy.c */
/* 0x0002 */ void aim_buddylist_reqrights(OscarData *, FlapConnection *);
-/* 0x0004 */ int aim_buddylist_set(OscarData *, FlapConnection *, const char *);
-/* 0x0004 */ int aim_buddylist_addbuddy(OscarData *, FlapConnection *, const char *);
-/* 0x0005 */ int aim_buddylist_removebuddy(OscarData *, FlapConnection *, const char *);
-
/* 0x000a - family_userlookup.c */
int aim_search_address(OscarData *, const char *);
-
-
-/* 0x000d - family_chatnav.c */
-/* 0x000e - family_chat.c */
-/* These apply to exchanges as well. */
-#define AIM_CHATROOM_FLAG_EVILABLE 0x0001
-#define AIM_CHATROOM_FLAG_NAV_ONLY 0x0002
-#define AIM_CHATROOM_FLAG_INSTANCING_ALLOWED 0x0004
-#define AIM_CHATROOM_FLAG_OCCUPANT_PEEK_ALLOWED 0x0008
-
struct aim_chat_exchangeinfo
{
guint16 number;
@@ -1191,41 +861,10 @@ struct aim_chat_exchangeinfo
#define AIM_CHATFLAGS_AWAY 0x0002
int aim_chat_send_im(OscarData *od, FlapConnection *conn, guint16 flags, const gchar *msg, int msglen, const char *encoding, const char *language);
int aim_chat_join(OscarData *od, guint16 exchange, const char *roomname, guint16 instance);
-int aim_chat_attachname(FlapConnection *conn, guint16 exchange, const char *roomname, guint16 instance);
-char *aim_chat_getname(FlapConnection *conn);
-FlapConnection *aim_chat_getconn(OscarData *, const char *name);
void aim_chatnav_reqrights(OscarData *od, FlapConnection *conn);
int aim_chatnav_createroom(OscarData *od, FlapConnection *conn, const char *name, guint16 exchange);
-int aim_chat_leaveroom(OscarData *od, const char *name);
-
-
-
-/* 0x000f - family_odir.c */
-struct aim_odir
-{
- char *first;
- char *last;
- char *middle;
- char *maiden;
- char *email;
- char *country;
- char *state;
- char *city;
- char *bn;
- char *interest;
- char *nick;
- char *zip;
- char *region;
- char *address;
- struct aim_odir *next;
-};
-
-int aim_odir_email(OscarData *, const char *, const char *);
-int aim_odir_name(OscarData *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *);
-int aim_odir_interest(OscarData *, const char *, const char *);
-
/* 0x0010 - family_bart.c */
@@ -1241,15 +880,9 @@ int aim_bart_request(OscarData *od, const char *bn, guint8 iconcsumtype, const g
#define AIM_SSI_TYPE_DENY 0x0003
#define AIM_SSI_TYPE_PDINFO 0x0004
#define AIM_SSI_TYPE_PRESENCEPREFS 0x0005
+#define AIM_SSI_TYPE_ICQDENY 0x000e
#define AIM_SSI_TYPE_ICONINFO 0x0014
-#define AIM_SSI_ACK_SUCCESS 0x0000
-#define AIM_SSI_ACK_ITEMNOTFOUND 0x0002
-#define AIM_SSI_ACK_IDNUMINUSE 0x000a
-#define AIM_SSI_ACK_ATMAX 0x000c
-#define AIM_SSI_ACK_INVALIDNAME 0x000d
-#define AIM_SSI_ACK_AUTHREQUIRED 0x000e
-
/* These flags are set in the 0x00c9 TLV of SSI type 0x0005 */
#define AIM_SSI_PRESENCE_FLAG_SHOWIDLE 0x00000400
#define AIM_SSI_PRESENCE_FLAG_NORECENTBUDDIES 0x00020000
@@ -1276,11 +909,9 @@ struct aim_ssi_tmp
/* These build the actual SNACs and queue them to be sent */
/* 0x0002 */ int aim_ssi_reqrights(OscarData *od);
/* 0x0004 */ int aim_ssi_reqdata(OscarData *od);
-/* 0x0005 */ int aim_ssi_reqifchanged(OscarData *od, time_t localstamp, guint16 localrev);
/* 0x0007 */ int aim_ssi_enable(OscarData *od);
/* 0x0011 */ int aim_ssi_modbegin(OscarData *od);
/* 0x0012 */ int aim_ssi_modend(OscarData *od);
-/* 0x0014 */ int aim_ssi_sendauth(OscarData *od, char *bn, char *msg);
/* 0x0018 */ int aim_ssi_sendauthrequest(OscarData *od, char *bn, const char *msg);
/* 0x001a */ int aim_ssi_sendauthreply(OscarData *od, char *bn, guint8 reply, const char *msg);
@@ -1297,49 +928,22 @@ gboolean aim_ssi_waitingforauth(struct aim_ssi_item *list, const char *gn, const
/* 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);
-int aim_ssi_addpermit(OscarData *od, const char *name);
-int aim_ssi_adddeny(OscarData *od, const char *name);
int aim_ssi_delbuddy(OscarData *od, const char *name, const char *group);
int aim_ssi_delgroup(OscarData *od, const char *group);
-int aim_ssi_delpermit(OscarData *od, const char *name);
-int aim_ssi_deldeny(OscarData *od, const char *name);
int aim_ssi_movebuddy(OscarData *od, const char *oldgn, const char *newgn, const char *bn);
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, guint32 vismask);
+int aim_ssi_setpermdeny(OscarData *od, guint8 permdeny);
int aim_ssi_setpresence(OscarData *od, guint32 presence);
int aim_ssi_seticon(OscarData *od, const guint8 *iconsum, guint8 iconsumlen);
int aim_ssi_delicon(OscarData *od);
+int aim_ssi_add_to_private_list(OscarData *od, const char* name, guint16 list_type);
+int aim_ssi_del_from_private_list(OscarData* od, const char* name, guint16 list_type);
-
-
-/* 0x0015 - family_icq.c */
-#define AIM_ICQ_INFO_SIMPLE 0x001
-#define AIM_ICQ_INFO_SUMMARY 0x002
-#define AIM_ICQ_INFO_EMAIL 0x004
-#define AIM_ICQ_INFO_PERSONAL 0x008
-#define AIM_ICQ_INFO_ADDITIONAL 0x010
-#define AIM_ICQ_INFO_WORK 0x020
-#define AIM_ICQ_INFO_INTERESTS 0x040
-#define AIM_ICQ_INFO_ORGS 0x080
-#define AIM_ICQ_INFO_UNKNOWN 0x100
-#define AIM_ICQ_INFO_HAVEALL 0x1ff
-
-#ifdef OLDSTYLE_ICQ_OFFLINEMSGS
-struct aim_icq_offlinemsg
-{
- guint32 sender;
- guint16 year;
- guint8 month, day, hour, minute;
- guint8 type;
- guint8 flags;
- char *msg;
- int msglen;
-};
-#endif /* OLDSTYLE_ICQ_OFFLINEMSGS */
+guint16 aim_ssi_getdenyentrytype(OscarData* od);
struct aim_icq_info
{
@@ -1396,22 +1000,17 @@ struct aim_icq_info
guint16 numaddresses;
char **email2;
- /* we keep track of these in a linked list because we're 1337 */
- struct aim_icq_info *next;
-
/* status note info */
guint8 icbm_cookie[8];
char *status_note_title;
+
+ gboolean for_auth_request;
+ char *auth_request_reason;
};
-#ifdef OLDSTYLE_ICQ_OFFLINEMSGS
-int aim_icq_reqofflinemsgs(OscarData *od);
-int aim_icq_ackofflinemsgs(OscarData *od);
-#endif
int aim_icq_setsecurity(OscarData *od, gboolean auth_required, gboolean webaware);
int aim_icq_changepasswd(OscarData *od, const char *passwd);
-int aim_icq_getsimpleinfo(OscarData *od, const char *uin);
-int aim_icq_getalias(OscarData *od, const char *uin);
+int aim_icq_getalias(OscarData *od, const char *uin, gboolean for_auth_request, char *auth_request_reason);
int aim_icq_getallinfo(OscarData *od, const char *uin);
int aim_icq_sendsms(OscarData *od, const char *name, const char *msg, const char *alias);
@@ -1545,22 +1144,19 @@ void aim_tlvlist_remove(GSList **list, const guint16 type);
(((*((buf)+2)) << 16) & 0x00ff0000) + \
(((*((buf)+3)) << 24) & 0xff000000))
+const char *oscar_get_msgerr_reason(size_t reason);
int oscar_get_ui_info_int(const char *str, int default_value);
const char *oscar_get_ui_info_string(const char *str, const char *default_value);
gchar *oscar_get_clientstring(void);
guint16 aimutil_iconsum(const guint8 *buf, int buflen);
-int aimutil_tokslen(char *toSearch, int theindex, char dl);
-int aimutil_itemcnt(char *toSearch, char dl);
-char *aimutil_itemindex(char *toSearch, int theindex, char dl);
gboolean oscar_util_valid_name(const char *bn);
gboolean oscar_util_valid_name_icq(const char *bn);
gboolean oscar_util_valid_name_sms(const char *bn);
int oscar_util_name_compare(const char *bn1, const char *bn2);
-
-
-
+gchar *oscar_util_format_string(const char *str, const char *name);
+gchar *oscar_format_buddies(GSList *buddies, const gchar *no_buddies_message);
typedef struct {
guint16 family;
@@ -1602,11 +1198,7 @@ int chatnav_modfirst(OscarData *od, aim_module_t *mod);
int chat_modfirst(OscarData *od, aim_module_t *mod);
int locate_modfirst(OscarData *od, aim_module_t *mod);
int service_modfirst(OscarData *od, aim_module_t *mod);
-int invite_modfirst(OscarData *od, aim_module_t *mod);
-int translate_modfirst(OscarData *od, aim_module_t *mod);
int popups_modfirst(OscarData *od, aim_module_t *mod);
-int adverts_modfirst(OscarData *od, aim_module_t *mod);
-int odir_modfirst(OscarData *od, aim_module_t *mod);
int bart_modfirst(OscarData *od, aim_module_t *mod);
int ssi_modfirst(OscarData *od, aim_module_t *mod);
int icq_modfirst(OscarData *od, aim_module_t *mod);
@@ -1615,15 +1207,14 @@ int email_modfirst(OscarData *od, aim_module_t *mod);
void aim_genericreq_n(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype);
void aim_genericreq_n_snacid(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype);
void aim_genericreq_l(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype, guint32 *);
-void aim_genericreq_s(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype, guint16 *);
/* bstream.c */
-int byte_stream_new(ByteStream *bs, guint32 len);
-int byte_stream_init(ByteStream *bs, guint8 *data, int len);
+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_empty(ByteStream *bs);
+int byte_stream_bytes_left(ByteStream *bs);
int byte_stream_curpos(ByteStream *bs);
-int byte_stream_setpos(ByteStream *bs, unsigned int off);
+int byte_stream_setpos(ByteStream *bs, size_t off);
void byte_stream_rewind(ByteStream *bs);
int byte_stream_advance(ByteStream *bs, int n);
guint8 byte_stream_get8(ByteStream *bs);
@@ -1632,18 +1223,18 @@ guint32 byte_stream_get32(ByteStream *bs);
guint8 byte_stream_getle8(ByteStream *bs);
guint16 byte_stream_getle16(ByteStream *bs);
guint32 byte_stream_getle32(ByteStream *bs);
-int byte_stream_getrawbuf(ByteStream *bs, guint8 *buf, int len);
-guint8 *byte_stream_getraw(ByteStream *bs, int len);
-char *byte_stream_getstr(ByteStream *bs, int len);
+int byte_stream_getrawbuf(ByteStream *bs, guint8 *buf, size_t len);
+guint8 *byte_stream_getraw(ByteStream *bs, size_t len);
+char *byte_stream_getstr(ByteStream *bs, size_t len);
int byte_stream_put8(ByteStream *bs, guint8 v);
int byte_stream_put16(ByteStream *bs, guint16 v);
int byte_stream_put32(ByteStream *bs, guint32 v);
int byte_stream_putle8(ByteStream *bs, guint8 v);
int byte_stream_putle16(ByteStream *bs, guint16 v);
int byte_stream_putle32(ByteStream *bs, guint32 v);
-int byte_stream_putraw(ByteStream *bs, const guint8 *v, int len);
+int byte_stream_putraw(ByteStream *bs, const guint8 *v, size_t len);
int byte_stream_putstr(ByteStream *bs, const char *str);
-int byte_stream_putbs(ByteStream *bs, ByteStream *srcbs, int len);
+int byte_stream_putbs(ByteStream *bs, ByteStream *srcbs, size_t len);
int byte_stream_putuid(ByteStream *bs, OscarData *od);
int byte_stream_putcaps(ByteStream *bs, guint64 caps);
@@ -1678,7 +1269,7 @@ aim_snacid_t aim_newsnac(OscarData *, aim_snac_t *newsnac);
aim_snacid_t aim_cachesnac(OscarData *od, const guint16 family, const guint16 type, const guint16 flags, const void *data, const int datalen);
aim_snac_t *aim_remsnac(OscarData *, aim_snacid_t id);
void aim_cleansnacs(OscarData *, int maxage);
-int aim_putsnac(ByteStream *, guint16 family, guint16 type, guint16 flags, aim_snacid_t id);
+int aim_putsnac(ByteStream *, guint16 family, guint16 type, aim_snacid_t id);
struct chatsnacinfo {
guint16 exchange;
@@ -1705,13 +1296,53 @@ IcbmCookie *aim_uncachecookie(OscarData *od, guint8 *cookie, int type);
IcbmCookie *aim_mkcookie(guint8 *, int, void *);
IcbmCookie *aim_checkcookie(OscarData *, const unsigned char *, const int);
int aim_freecookie(OscarData *od, IcbmCookie *cookie);
-int aim_msgcookie_gettype(guint64 type);
int aim_cookie_free(OscarData *od, IcbmCookie *cookie);
int aim_chat_readroominfo(ByteStream *bs, struct aim_chat_roominfo *outinfo);
void flap_connection_destroy_chat(OscarData *od, FlapConnection *conn);
+/* userinfo.c - displaying user information */
+
+void oscar_user_info_append_status(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo, gboolean strip_html_tags);
+void oscar_user_info_append_extra_info(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo);
+void oscar_user_info_display_error(OscarData *od, guint16 error_reason, char *buddy);
+void oscar_user_info_display_icq(OscarData *od, struct aim_icq_info *info);
+void oscar_user_info_display_aim(OscarData *od, aim_userinfo_t *userinfo);
+
+/* authorization.c - OSCAR authorization requests */
+void oscar_auth_sendrequest(PurpleConnection *gc, const char *name);
+void oscar_auth_sendrequest_menu(PurpleBlistNode *node, gpointer ignored);
+void oscar_auth_recvrequest(PurpleConnection *gc, gchar *name, gchar *nick, gchar *reason);
+
+void oscar_set_aim_permdeny(PurpleConnection *gc);
+
+struct buddyinfo
+{
+ gboolean typingnot;
+ guint32 ipaddr;
+
+ unsigned long ico_me_len;
+ unsigned long ico_me_csum;
+ time_t ico_me_time;
+ gboolean ico_informed;
+
+ unsigned long ico_len;
+ unsigned long ico_csum;
+ time_t ico_time;
+ gboolean ico_need;
+ gboolean ico_sent;
+};
+
+struct name_data
+{
+ PurpleConnection *gc;
+ gchar *name;
+ gchar *nick;
+};
+
+void oscar_free_name_data(struct name_data *data);
+
#ifdef __cplusplus
}
#endif
diff --git a/libpurple/protocols/oscar/oscar_data.c b/libpurple/protocols/oscar/oscar_data.c
index dcc43baa5d..8c0a9e17d9 100644
--- a/libpurple/protocols/oscar/oscar_data.c
+++ b/libpurple/protocols/oscar/oscar_data.c
@@ -53,17 +53,13 @@ oscar_data_new(void)
aim__registermodule(od, locate_modfirst);
aim__registermodule(od, buddylist_modfirst);
aim__registermodule(od, msg_modfirst);
- /* aim__registermodule(od, adverts_modfirst); */
- /* aim__registermodule(od, invite_modfirst); */
aim__registermodule(od, admin_modfirst);
aim__registermodule(od, popups_modfirst);
aim__registermodule(od, bos_modfirst);
aim__registermodule(od, search_modfirst);
aim__registermodule(od, stats_modfirst);
- /* aim__registermodule(od, translate_modfirst); */
aim__registermodule(od, chatnav_modfirst);
aim__registermodule(od, chat_modfirst);
- aim__registermodule(od, odir_modfirst);
aim__registermodule(od, bart_modfirst);
/* missing 0x11 - 0x12 */
aim__registermodule(od, ssi_modfirst);
diff --git a/libpurple/protocols/oscar/oscarcommon.h b/libpurple/protocols/oscar/oscarcommon.h
index a7af4cb62f..52433e5f99 100644
--- a/libpurple/protocols/oscar/oscarcommon.h
+++ b/libpurple/protocols/oscar/oscarcommon.h
@@ -77,7 +77,6 @@ void oscar_add_permit(PurpleConnection *gc, const char *who);
void oscar_add_deny(PurpleConnection *gc, const char *who);
void oscar_rem_permit(PurpleConnection *gc, const char *who);
void oscar_rem_deny(PurpleConnection *gc, const char *who);
-void oscar_set_permit_deny(PurpleConnection *gc);
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);
diff --git a/libpurple/protocols/oscar/peer_proxy.c b/libpurple/protocols/oscar/peer_proxy.c
index 1ef991b704..6bf5c07c80 100644
--- a/libpurple/protocols/oscar/peer_proxy.c
+++ b/libpurple/protocols/oscar/peer_proxy.c
@@ -32,8 +32,8 @@ peer_proxy_send(PeerConnection *conn, ProxyFrame *frame)
ByteStream bs;
purple_debug_info("oscar", "Outgoing peer proxy frame with "
- "type=0x%04hx, unknown=0x%08x, "
- "flags=0x%04hx, and payload length=%hd\n",
+ "type=0x%04hx, unknown=0x%08x, flags=0x%04hx, and "
+ "payload length=%" G_GSIZE_FORMAT "\n",
frame->type, frame->unknown,
frame->flags, frame->payload.len);
@@ -129,8 +129,8 @@ static void
peer_proxy_recv_frame(PeerConnection *conn, ProxyFrame *frame)
{
purple_debug_info("oscar", "Incoming peer proxy frame with "
- "type=0x%04hx, unknown=0x%08x, "
- "flags=0x%04hx, and payload length=%hd\n", frame->type,
+ "type=0x%04hx, unknown=0x%08x, flags=0x%04hx, and "
+ "payload length=%" G_GSIZE_FORMAT "\n", frame->type,
frame->unknown, frame->flags, frame->payload.len);
if (frame->type == PEER_PROXY_TYPE_CREATED)
@@ -168,7 +168,7 @@ peer_proxy_recv_frame(PeerConnection *conn, ProxyFrame *frame)
}
else if (frame->type == PEER_PROXY_TYPE_ERROR)
{
- if (byte_stream_empty(&frame->payload) >= 2)
+ if (byte_stream_bytes_left(&frame->payload) >= 2)
{
guint16 error;
const char *msg;
diff --git a/libpurple/protocols/oscar/rxhandlers.c b/libpurple/protocols/oscar/rxhandlers.c
index d378624d35..75675a9fbe 100644
--- a/libpurple/protocols/oscar/rxhandlers.c
+++ b/libpurple/protocols/oscar/rxhandlers.c
@@ -95,194 +95,3 @@ void aim__shutdownmodules(OscarData *od)
return;
}
-
-#if 0
-/*
- * Bleck functions get called when there's no non-bleck functions
- * around to cleanup the mess...
- */
-static int bleck(OscarData *od, FlapFrame *frame, ...)
-{
- guint16 family, subtype;
- guint16 maxf, maxs;
-
- static const char *channels[6] = {
- "Invalid (0)",
- "FLAP Version",
- "SNAC",
- "Invalid (3)",
- "Negotiation",
- "FLAP NOP"
- };
- static const int maxchannels = 5;
-
- /* XXX: this is ugly. and big just for debugging. */
- static const char *literals[14][25] = {
- {"Invalid",
- NULL
- },
- {"General",
- "Invalid",
- "Error",
- "Client Ready",
- "Server Ready",
- "Service Request",
- "Redirect",
- "Rate Information Request",
- "Rate Information",
- "Rate Information Ack",
- NULL,
- "Rate Information Change",
- "Server Pause",
- NULL,
- "Server Resume",
- "Request Personal User Information",
- "Personal User Information",
- "Evil Notification",
- NULL,
- "Migration notice",
- "Message of the Day",
- "Set Privacy Flags",
- "Well Known URL",
- "NOP"
- },
- {"Location",
- "Invalid",
- "Error",
- "Request Rights",
- "Rights Information",
- "Set user information",
- "Request User Information",
- "User Information",
- "Watcher Sub Request",
- "Watcher Notification"
- },
- {"Buddy List Management",
- "Invalid",
- "Error",
- "Request Rights",
- "Rights Information",
- "Add Buddy",
- "Remove Buddy",
- "Watcher List Query",
- "Watcher List Response",
- "Watcher SubRequest",
- "Watcher Notification",
- "Reject Notification",
- "Oncoming Buddy",
- "Offgoing Buddy"
- },
- {"Messeging",
- "Invalid",
- "Error",
- "Add ICBM Parameter",
- "Remove ICBM Parameter",
- "Request Parameter Information",
- "Parameter Information",
- "Outgoing Message",
- "Incoming Message",
- "Evil Request",
- "Evil Reply",
- "Missed Calls",
- "Message Error",
- "Host Ack"
- },
- {"Advertisements",
- "Invalid",
- "Error",
- "Request Ad",
- "Ad Data (GIFs)"
- },
- {"Invitation / Client-to-Client",
- "Invalid",
- "Error",
- "Invite a Friend",
- "Invitation Ack"
- },
- {"Administrative",
- "Invalid",
- "Error",
- "Information Request",
- "Information Reply",
- "Information Change Request",
- "Information Chat Reply",
- "Account Confirm Request",
- "Account Confirm Reply",
- "Account Delete Request",
- "Account Delete Reply"
- },
- {"Popups",
- "Invalid",
- "Error",
- "Display Popup"
- },
- {"BOS",
- "Invalid",
- "Error",
- "Request Rights",
- "Rights Response",
- "Set group permission mask",
- "Add permission list entries",
- "Delete permission list entries",
- "Add deny list entries",
- "Delete deny list entries",
- "Server Error"
- },
- {"User Lookup",
- "Invalid",
- "Error",
- "Search Request",
- "Search Response"
- },
- {"Stats",
- "Invalid",
- "Error",
- "Set minimum report interval",
- "Report Events"
- },
- {"Translate",
- "Invalid",
- "Error",
- "Translate Request",
- "Translate Reply",
- },
- {"Chat Navigation",
- "Invalid",
- "Error",
- "Request rights",
- "Request Exchange Information",
- "Request Room Information",
- "Request Occupant List",
- "Search for Room",
- "Outgoing Message",
- "Incoming Message",
- "Evil Request",
- "Evil Reply",
- "Chat Error",
- }
- };
-
- maxf = sizeof(literals) / sizeof(literals[0]);
- maxs = sizeof(literals[0]) / sizeof(literals[0][0]);
-
- if (frame->channel == 0x02) {
-
- family = byte_stream_get16(&frame->data);
- subtype = byte_stream_get16(&frame->data);
-
- if ((family < maxf) && (subtype+1 < maxs) && (literals[family][subtype] != NULL))
- purple_debug_misc("oscar", "bleck: channel %s: null handler for %04x/%04x (%s)\n", channels[frame->channel], family, subtype, literals[family][subtype+1]);
- else
- purple_debug_misc("oscar", "bleck: channel %s: null handler for %04x/%04x (no literal)\n", channels[frame->channel], family, subtype);
- } else {
-
- if (frame->channel <= maxchannels)
- purple_debug_misc("oscar", "bleck: channel %s (0x%02x)\n", channels[frame->channel], frame->channel);
- else
- purple_debug_misc("oscar", "bleck: unknown channel 0x%02x\n", frame->channel);
-
- }
-
- return 1;
-}
-#endif
diff --git a/libpurple/protocols/oscar/snac.c b/libpurple/protocols/oscar/snac.c
index 4988037f3e..451dfc6647 100644
--- a/libpurple/protocols/oscar/snac.c
+++ b/libpurple/protocols/oscar/snac.c
@@ -56,11 +56,9 @@ aim_snacid_t aim_cachesnac(OscarData *od, const guint16 family, const guint16 ty
snac.type = type;
snac.flags = flags;
- if (datalen) {
- if (!(snac.data = g_malloc(datalen)))
- return 0; /* er... */
- memcpy(snac.data, data, datalen);
- } else
+ if (datalen)
+ snac.data = g_memdup(data, datalen);
+ else
snac.data = NULL;
return aim_newsnac(od, &snac);
@@ -78,9 +76,7 @@ aim_snacid_t aim_newsnac(OscarData *od, aim_snac_t *newsnac)
if (!newsnac)
return 0;
- if (!(snac = g_malloc(sizeof(aim_snac_t))))
- return 0;
- memcpy(snac, newsnac, sizeof(aim_snac_t));
+ snac = g_memdup(newsnac, sizeof(aim_snac_t));
snac->issuetime = time(NULL);
index = snac->id % FAIM_SNAC_HASH_SIZE;
@@ -155,12 +151,12 @@ void aim_cleansnacs(OscarData *od, int maxage)
return;
}
-int aim_putsnac(ByteStream *bs, guint16 family, guint16 subtype, guint16 flags, aim_snacid_t snacid)
+int aim_putsnac(ByteStream *bs, guint16 family, guint16 subtype, aim_snacid_t snacid)
{
byte_stream_put16(bs, family);
byte_stream_put16(bs, subtype);
- byte_stream_put16(bs, flags);
+ byte_stream_put16(bs, 0x0000);
byte_stream_put32(bs, snacid);
return 10;
diff --git a/libpurple/protocols/oscar/tlv.c b/libpurple/protocols/oscar/tlv.c
index 8c93f98005..67f18080c3 100644
--- a/libpurple/protocols/oscar/tlv.c
+++ b/libpurple/protocols/oscar/tlv.c
@@ -49,27 +49,7 @@ aim_tlv_read(GSList *list, ByteStream *bs)
type = byte_stream_get16(bs);
length = byte_stream_get16(bs);
-#if 0
- /*
- * This code hasn't been needed in years. It's been commented
- * out since 2003, at the latest. It seems likely that it was
- * just a bug in their server code that has since been fixed.
- * In any case, here's the orignal comment, kept for historical
- * purposes:
- *
- * Okay, so now AOL has decided that any TLV of
- * type 0x0013 can only be two bytes, despite
- * what the actual given length is. So here
- * we dump any invalid TLVs of that sort. Hopefully
- * there's no special cases to this special case.
- * - mid (30jun2000)
- */
- if ((type == 0x0013) && (length != 0x0002)) {
- length = 0x0002;
- return list;
- }
-#endif
- if (length > byte_stream_empty(bs)) {
+ if (length > byte_stream_bytes_left(bs)) {
aim_tlvlist_free(list);
return NULL;
}
@@ -108,7 +88,7 @@ GSList *aim_tlvlist_read(ByteStream *bs)
{
GSList *list = NULL;
- while (byte_stream_empty(bs) > 0) {
+ while (byte_stream_bytes_left(bs) > 0) {
list = aim_tlv_read(list, bs);
if (list == NULL)
return NULL;
@@ -142,7 +122,7 @@ GSList *aim_tlvlist_readnum(ByteStream *bs, guint16 num)
{
GSList *list = NULL;
- while ((byte_stream_empty(bs) > 0) && (num != 0)) {
+ while ((byte_stream_bytes_left(bs) > 0) && (num != 0)) {
list = aim_tlv_read(list, bs);
if (list == NULL)
return NULL;
@@ -177,7 +157,7 @@ GSList *aim_tlvlist_readlen(ByteStream *bs, guint16 len)
{
GSList *list = NULL;
- while ((byte_stream_empty(bs) > 0) && (len > 0)) {
+ while ((byte_stream_bytes_left(bs) > 0) && (len > 0)) {
list = aim_tlv_read(list, bs);
if (list == NULL)
return NULL;
@@ -391,6 +371,17 @@ int aim_tlvlist_add_str(GSList **list, const guint16 type, const char *value)
return aim_tlvlist_add_raw(list, type, strlen(value), (guint8 *)value);
}
+static int
+count_caps(guint64 caps)
+{
+ int set_bits = 0;
+ while (caps) {
+ set_bits += caps & 1;
+ caps >>= 1;
+ }
+ return set_bits;
+}
+
/**
* Adds a block of capability blocks to a TLV chain. The bitfield
* passed in should be a bitwise %OR of any of the %AIM_CAPS constants:
@@ -409,23 +400,24 @@ int aim_tlvlist_add_str(GSList **list, const guint16 type, const char *value)
*/
int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint64 caps, const char *mood)
{
- guint8 buf[256]; /* TODO: Don't use a fixed length buffer */
ByteStream bs;
+ guint32 bs_size;
guint8 *data;
if (caps == 0)
return 0; /* nothing there anyway */
- byte_stream_init(&bs, buf, sizeof(buf));
+ data = icq_get_custom_icon_data(mood);
+ bs_size = 16*(count_caps(caps) + (data != NULL ? 1 : 0));
+ byte_stream_new(&bs, bs_size);
byte_stream_putcaps(&bs, caps);
-
+
/* adding of custom icon GUID */
- data = icq_get_custom_icon_data(mood);
if (data != NULL)
byte_stream_putraw(&bs, data, 16);
- return aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), buf);
+ return aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), bs.data);
}
/**
@@ -668,7 +660,7 @@ int aim_tlvlist_write(ByteStream *bs, GSList **list)
/* do an initial run to test total length */
goodbuflen = aim_tlvlist_size(*list);
- if (goodbuflen > byte_stream_empty(bs))
+ if (goodbuflen > byte_stream_bytes_left(bs))
return 0; /* not enough buffer */
/* do the real write-out */
diff --git a/libpurple/protocols/oscar/userinfo.c b/libpurple/protocols/oscar/userinfo.c
new file mode 100644
index 0000000000..5762977ad6
--- /dev/null
+++ b/libpurple/protocols/oscar/userinfo.c
@@ -0,0 +1,553 @@
+/*
+ * Purple's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
+*/
+
+/*
+ * Displaying various information about buddies.
+ */
+
+#include "encoding.h"
+#include "oscar.h"
+
+static gchar *
+oscar_caps_to_string(guint64 caps)
+{
+ GString *str;
+ const gchar *tmp;
+ guint64 bit = 1;
+
+ str = g_string_new("");
+
+ if (!caps) {
+ return NULL;
+ } else while (bit <= OSCAR_CAPABILITY_LAST) {
+ if (bit & caps) {
+ switch (bit) {
+ case OSCAR_CAPABILITY_BUDDYICON:
+ tmp = _("Buddy Icon");
+ break;
+ case OSCAR_CAPABILITY_TALK:
+ tmp = _("Voice");
+ break;
+ case OSCAR_CAPABILITY_DIRECTIM:
+ tmp = _("AIM Direct IM");
+ break;
+ case OSCAR_CAPABILITY_CHAT:
+ tmp = _("Chat");
+ break;
+ case OSCAR_CAPABILITY_GETFILE:
+ tmp = _("Get File");
+ break;
+ case OSCAR_CAPABILITY_SENDFILE:
+ tmp = _("Send File");
+ break;
+ case OSCAR_CAPABILITY_GAMES:
+ case OSCAR_CAPABILITY_GAMES2:
+ tmp = _("Games");
+ break;
+ case OSCAR_CAPABILITY_XTRAZ:
+ case OSCAR_CAPABILITY_NEWCAPS:
+ tmp = _("ICQ Xtraz");
+ break;
+ case OSCAR_CAPABILITY_ADDINS:
+ tmp = _("Add-Ins");
+ break;
+ case OSCAR_CAPABILITY_SENDBUDDYLIST:
+ tmp = _("Send Buddy List");
+ break;
+ case OSCAR_CAPABILITY_ICQ_DIRECT:
+ tmp = _("ICQ Direct Connect");
+ break;
+ case OSCAR_CAPABILITY_APINFO:
+ tmp = _("AP User");
+ break;
+ case OSCAR_CAPABILITY_ICQRTF:
+ tmp = _("ICQ RTF");
+ break;
+ case OSCAR_CAPABILITY_EMPTY:
+ tmp = _("Nihilist");
+ break;
+ case OSCAR_CAPABILITY_ICQSERVERRELAY:
+ tmp = _("ICQ Server Relay");
+ break;
+ case OSCAR_CAPABILITY_UNICODEOLD:
+ tmp = _("Old ICQ UTF8");
+ break;
+ case OSCAR_CAPABILITY_TRILLIANCRYPT:
+ tmp = _("Trillian Encryption");
+ break;
+ case OSCAR_CAPABILITY_UNICODE:
+ tmp = _("ICQ UTF8");
+ break;
+ case OSCAR_CAPABILITY_HIPTOP:
+ tmp = _("Hiptop");
+ break;
+ case OSCAR_CAPABILITY_SECUREIM:
+ tmp = _("Security Enabled");
+ break;
+ case OSCAR_CAPABILITY_VIDEO:
+ tmp = _("Video Chat");
+ break;
+ /* Not actually sure about this one... WinAIM doesn't show anything */
+ case OSCAR_CAPABILITY_ICHATAV:
+ tmp = _("iChat AV");
+ break;
+ case OSCAR_CAPABILITY_LIVEVIDEO:
+ tmp = _("Live Video");
+ break;
+ case OSCAR_CAPABILITY_CAMERA:
+ tmp = _("Camera");
+ break;
+ case OSCAR_CAPABILITY_ICHAT_SCREENSHARE:
+ tmp = _("Screen Sharing");
+ break;
+ default:
+ tmp = NULL;
+ break;
+ }
+ if (tmp)
+ g_string_append_printf(str, "%s%s", (*(str->str) == '\0' ? "" : ", "), tmp);
+ }
+ bit <<= 1;
+ }
+
+ return g_string_free(str, FALSE);
+}
+
+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);
+ g_free(utf8);
+ }
+}
+
+static void
+oscar_user_info_convert_and_add_hyperlink(PurpleAccount *account, OscarData *od, PurpleNotifyUserInfo *user_info,
+ const char *name, const char *value, const char *url_prefix)
+{
+ gchar *utf8;
+
+ 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);
+ g_free(utf8);
+ g_free(tmp);
+ }
+}
+
+/**
+ * @brief Append the status information to a user_info struct
+ *
+ * The returned information is HTML-ready, appropriately escaped, as all information in a user_info struct should be HTML.
+ *
+ * @param gc The PurpleConnection
+ * @param user_info A PurpleNotifyUserInfo object to which status information will be added
+ * @param b The PurpleBuddy whose status is desired. This or the aim_userinfo_t (or both) must be passed to oscar_user_info_append_status().
+ * @param userinfo The aim_userinfo_t of the buddy whose status is desired. This or the PurpleBuddy (or both) must be passed to oscar_user_info_append_status().
+ * @param strip_html_tags If strip_html_tags is TRUE, tags embedded in the status message will be stripped, returning a non-formatted string. The string will still be HTML escaped.
+ */
+void
+oscar_user_info_append_status(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo, gboolean strip_html_tags)
+{
+ PurpleAccount *account = purple_connection_get_account(gc);
+ OscarData *od;
+ PurplePresence *presence = NULL;
+ PurpleStatus *status = NULL;
+ gchar *message = NULL, *itmsurl = NULL, *tmp;
+ gboolean is_away;
+
+ od = purple_connection_get_protocol_data(gc);
+
+ if (b == NULL && userinfo == NULL)
+ return;
+
+ if (b == NULL)
+ b = purple_find_buddy(purple_connection_get_account(gc), userinfo->bn);
+ else
+ userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b));
+
+ if (b) {
+ presence = purple_buddy_get_presence(b);
+ status = purple_presence_get_active_status(presence);
+ }
+
+ /* If we have both b and userinfo we favor userinfo, because if we're
+ viewing someone's profile then we want the HTML away message, and
+ the "message" attribute of the status contains only the plaintext
+ message. */
+ if (userinfo) {
+ if ((userinfo->flags & AIM_FLAG_AWAY) && userinfo->away_len > 0 && userinfo->away != NULL && userinfo->away_encoding != NULL) {
+ /* Away message */
+ message = oscar_encoding_to_utf8(userinfo->away_encoding, userinfo->away, userinfo->away_len);
+ } else {
+ /*
+ * Available message or non-HTML away message (because that's
+ * all we have right now.
+ */
+ if ((userinfo->status != NULL) && userinfo->status[0] != '\0') {
+ message = oscar_encoding_to_utf8(userinfo->status_encoding, userinfo->status, userinfo->status_len);
+ }
+#if defined (_WIN32) || defined (__APPLE__)
+ if (userinfo->itmsurl && (userinfo->itmsurl[0] != '\0')) {
+ itmsurl = oscar_encoding_to_utf8(userinfo->itmsurl_encoding, userinfo->itmsurl, userinfo->itmsurl_len);
+ }
+#endif
+ }
+ } else {
+ message = g_strdup(purple_status_get_attr_string(status, "message"));
+ itmsurl = g_strdup(purple_status_get_attr_string(status, "itmsurl"));
+ }
+
+ is_away = ((status && !purple_status_is_available(status)) ||
+ (userinfo && (userinfo->flags & AIM_FLAG_AWAY)));
+
+ if (strip_html_tags) {
+ /* Away messages are HTML, but available messages were originally plain text.
+ * We therefore need to strip away messages but not available messages if we're asked to remove HTML tags.
+ */
+ /*
+ * It seems like the above comment no longer applies. All messages need
+ * to be escaped.
+ */
+ if (message) {
+ gchar *tmp2;
+ tmp = purple_markup_strip_html(message);
+ g_free(message);
+ tmp2 = g_markup_escape_text(tmp, -1);
+ g_free(tmp);
+ message = tmp2;
+ }
+
+ } else {
+ if (itmsurl) {
+ tmp = g_strdup_printf("<a href=\"%s\">%s</a>",
+ itmsurl, message);
+ g_free(message);
+ message = tmp;
+ }
+ }
+ g_free(itmsurl);
+
+ if (message) {
+ tmp = oscar_util_format_string(message, purple_account_get_username(account));
+ g_free(message);
+ message = tmp;
+ }
+
+ if (b) {
+ if (purple_presence_is_online(presence)) {
+ if (oscar_util_valid_name_icq(purple_buddy_get_name(b)) || is_away || !message || !(*message)) {
+ /* Append the status name for online ICQ statuses, away AIM statuses, and for all buddies with no message.
+ * If the status name and the message are the same, only show one. */
+ const char *status_name = purple_status_get_name(status);
+ if (status_name && message && !strcmp(status_name, message))
+ status_name = NULL;
+
+ tmp = g_strdup_printf("%s%s%s",
+ status_name ? status_name : "",
+ ((status_name && message) && *message) ? ": " : "",
+ (message && *message) ? message : "");
+ g_free(message);
+ message = tmp;
+ }
+
+ } 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 */
+ tmp = g_strdup_printf("%s%s%s",
+ _("Not Authorized"),
+ (message && *message) ? ": " : "",
+ (message && *message) ? message : "");
+ g_free(message);
+ message = tmp;
+ } else {
+ g_free(message);
+ message = g_strdup(_("Offline"));
+ }
+ }
+
+ if (presence) {
+ const char *mood;
+ const char *description;
+ status = purple_presence_get_status(presence, "mood");
+ mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME);
+ description = icq_get_custom_icon_description(mood);
+ if (description && *description)
+ purple_notify_user_info_add_pair(user_info, _("Mood"), _(description));
+ }
+
+ purple_notify_user_info_add_pair(user_info, _("Status"), message);
+ g_free(message);
+}
+
+void
+oscar_user_info_append_extra_info(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo)
+{
+ OscarData *od;
+ PurpleAccount *account;
+ PurplePresence *presence = NULL;
+ PurpleStatus *status = NULL;
+ PurpleGroup *g = NULL;
+ struct buddyinfo *bi = NULL;
+ char *tmp;
+ const char *bname = NULL, *gname = NULL;
+
+ od = purple_connection_get_protocol_data(gc);
+ account = purple_connection_get_account(gc);
+
+ if ((user_info == NULL) || ((b == NULL) && (userinfo == NULL)))
+ return;
+
+ if (userinfo == NULL)
+ userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b));
+
+ if (b == NULL)
+ b = purple_find_buddy(account, userinfo->bn);
+
+ if (b != NULL) {
+ bname = purple_buddy_get_name(b);
+ g = purple_buddy_get_group(b);
+ gname = purple_group_get_name(g);
+ presence = purple_buddy_get_presence(b);
+ status = purple_presence_get_active_status(presence);
+ }
+
+ if (userinfo != NULL)
+ bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, userinfo->bn));
+
+ if ((bi != NULL) && (bi->ipaddr != 0)) {
+ tmp = g_strdup_printf("%hhu.%hhu.%hhu.%hhu",
+ (bi->ipaddr & 0xff000000) >> 24,
+ (bi->ipaddr & 0x00ff0000) >> 16,
+ (bi->ipaddr & 0x0000ff00) >> 8,
+ (bi->ipaddr & 0x000000ff));
+ oscar_user_info_add_pair(user_info, _("IP Address"), tmp);
+ g_free(tmp);
+ }
+
+ 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);
+ }
+
+ if ((b != NULL) && (bname != NULL) && (g != NULL) && (gname != NULL)) {
+ tmp = aim_ssi_getcomment(od->ssi.local, gname, bname);
+ if (tmp != NULL) {
+ char *tmp2 = g_markup_escape_text(tmp, strlen(tmp));
+ g_free(tmp);
+
+ oscar_user_info_convert_and_add(account, od, user_info, _("Buddy Comment"), tmp2);
+ g_free(tmp2);
+ }
+ }
+}
+
+void
+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_userinfo(od->gc, buddy, user_info, NULL, NULL);
+ purple_notify_user_info_destroy(user_info);
+ purple_conv_present_error(buddy, purple_connection_get_account(od->gc), buf);
+ g_free(buf);
+}
+
+void
+oscar_user_info_display_icq(OscarData *od, struct aim_icq_info *info)
+{
+ PurpleConnection *gc = od->gc;
+ PurpleAccount *account = purple_connection_get_account(gc);
+ PurpleBuddy *buddy;
+ struct buddyinfo *bi;
+ gchar who[16];
+ PurpleNotifyUserInfo *user_info;
+ const gchar *alias;
+
+ if (!info->uin)
+ return;
+
+ user_info = purple_notify_user_info_new();
+
+ g_snprintf(who, sizeof(who), "%u", info->uin);
+ buddy = purple_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);
+ oscar_user_info_convert_and_add(account, od, user_info, _("Nick"), info->nick);
+ if ((bi != NULL) && (bi->ipaddr != 0)) {
+ char *tstr = g_strdup_printf("%hhu.%hhu.%hhu.%hhu",
+ (bi->ipaddr & 0xff000000) >> 24,
+ (bi->ipaddr & 0x00ff0000) >> 16,
+ (bi->ipaddr & 0x0000ff00) >> 8,
+ (bi->ipaddr & 0x000000ff));
+ purple_notify_user_info_add_pair(user_info, _("IP Address"), tstr);
+ g_free(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);
+ oscar_user_info_convert_and_add_hyperlink(account, od, user_info, _("Email Address"), info->email, "mailto:");
+ if (info->numaddresses && info->email2) {
+ int i;
+ for (i = 0; i < info->numaddresses; i++) {
+ oscar_user_info_convert_and_add_hyperlink(account, od, user_info, _("Email Address"), info->email2[i], "mailto:");
+ }
+ }
+ 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")));
+
+ if ((info->birthyear > 1900) && (info->birthmonth > 0) && (info->birthday > 0)) {
+ /* Initialize the struct properly or strftime() will crash
+ * under some conditions (e.g. Debian sarge w/ LANG=en_HK). */
+ time_t t = time(NULL);
+ struct tm *tm = localtime(&t);
+
+ tm->tm_mday = (int)info->birthday;
+ tm->tm_mon = (int)info->birthmonth - 1;
+ tm->tm_year = (int)info->birthyear - 1900;
+
+ /* To be 100% sure that the fields are re-normalized.
+ * If you're sure strftime() ALWAYS does this EVERYWHERE,
+ * feel free to remove it. --rlaager */
+ mktime(tm);
+
+ oscar_user_info_convert_and_add(account, od, user_info, _("Birthday"), purple_date_format_short(tm));
+ }
+ 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);
+ }
+ 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, /* strip_html_tags */ FALSE);
+
+ oscar_user_info_convert_and_add(account, od, user_info, _("Additional Information"), info->info);
+ purple_notify_user_info_add_section_break(user_info);
+
+ if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) {
+ purple_notify_user_info_add_section_header(user_info, _("Home Address"));
+
+ oscar_user_info_convert_and_add(account, od, user_info, _("Address"), info->homeaddr);
+ oscar_user_info_convert_and_add(account, od, user_info, _("City"), info->homecity);
+ oscar_user_info_convert_and_add(account, od, user_info, _("State"), info->homestate);
+ oscar_user_info_convert_and_add(account, od, user_info, _("Zip Code"), info->homezip);
+ }
+ if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
+ purple_notify_user_info_add_section_header(user_info, _("Work Address"));
+
+ oscar_user_info_convert_and_add(account, od, user_info, _("Address"), info->workaddr);
+ oscar_user_info_convert_and_add(account, od, user_info, _("City"), info->workcity);
+ oscar_user_info_convert_and_add(account, od, user_info, _("State"), info->workstate);
+ oscar_user_info_convert_and_add(account, od, user_info, _("Zip Code"), info->workzip);
+ }
+ if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
+ purple_notify_user_info_add_section_header(user_info, _("Work Information"));
+
+ 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);
+ oscar_user_info_convert_and_add_hyperlink(account, od, user_info, _("Web Page"), info->email, "");
+ }
+
+ if (buddy != NULL)
+ alias = purple_buddy_get_alias(buddy);
+ else
+ alias = who;
+ purple_notify_userinfo(gc, who, user_info, NULL, NULL);
+ purple_notify_user_info_destroy(user_info);
+}
+
+void
+oscar_user_info_display_aim(OscarData *od, aim_userinfo_t *userinfo)
+{
+ PurpleConnection *gc = od->gc;
+ PurpleAccount *account = purple_connection_get_account(gc);
+ PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
+ gchar *tmp = NULL, *info_utf8 = NULL, *base_profile_url = NULL;
+
+ oscar_user_info_append_status(gc, user_info, /* PurpleBuddy */ NULL, userinfo, /* strip_html_tags */ FALSE);
+
+ 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);
+ g_free(tmp);
+ }
+
+ oscar_user_info_append_extra_info(gc, user_info, NULL, 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)));
+ }
+
+ 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)));
+ }
+
+ if (userinfo->capabilities != 0) {
+ tmp = oscar_caps_to_string(userinfo->capabilities);
+ oscar_user_info_add_pair(user_info, _("Capabilities"), tmp);
+ g_free(tmp);
+ }
+
+ /* Info */
+ if ((userinfo->info_len > 0) && (userinfo->info != NULL) && (userinfo->info_encoding != NULL)) {
+ 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);
+ g_free(tmp);
+ g_free(info_utf8);
+ }
+
+ purple_notify_user_info_add_section_break(user_info);
+ 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);
+ g_free(tmp);
+
+ purple_notify_userinfo(gc, userinfo->bn, user_info, NULL, NULL);
+ purple_notify_user_info_destroy(user_info);
+} \ No newline at end of file
diff --git a/libpurple/protocols/oscar/util.c b/libpurple/protocols/oscar/util.c
index b8b1cf7c2e..81211824eb 100644
--- a/libpurple/protocols/oscar/util.c
+++ b/libpurple/protocols/oscar/util.c
@@ -35,6 +35,40 @@
#include "win32dep.h"
#endif
+static const char * const msgerrreason[] = {
+ N_("Invalid error"),
+ N_("Invalid SNAC"),
+ N_("Server rate limit exceeded"),
+ N_("Client rate limit exceeded"),
+ N_("Not logged in"),
+ N_("Service unavailable"),
+ N_("Service not defined"),
+ N_("Obsolete SNAC"),
+ N_("Not supported by host"),
+ N_("Not supported by client"),
+ N_("Refused by client"),
+ N_("Reply too big"),
+ N_("Responses lost"),
+ N_("Request denied"),
+ N_("Busted SNAC payload"),
+ N_("Insufficient rights"),
+ N_("In local permit/deny"),
+ N_("Warning level too high (sender)"),
+ N_("Warning level too high (receiver)"),
+ N_("User temporarily unavailable"),
+ N_("No match"),
+ N_("List overflow"),
+ N_("Request ambiguous"),
+ N_("Queue full"),
+ N_("Not while on AOL")
+};
+static const int msgerrreasonlen = G_N_ELEMENTS(msgerrreason);
+
+const char *oscar_get_msgerr_reason(size_t reason)
+{
+ return (reason < msgerrreasonlen) ? _(msgerrreason[reason]) : _("Unknown reason");
+}
+
int oscar_get_ui_info_int(const char *str, int default_value)
{
GHashTable *ui_info;
@@ -73,91 +107,6 @@ gchar *oscar_get_clientstring(void)
return g_strdup_printf("%s/%s", name, version);;
}
-/*
- * Tokenizing functions. Used to portably replace strtok/sep.
- * -- DMP.
- *
- */
-/* TODO: Get rid of this and use glib functions */
-int
-aimutil_tokslen(char *toSearch, int theindex, char dl)
-{
- int curCount = 1;
- char *next;
- char *last;
- int toReturn;
-
- last = toSearch;
- next = strchr(toSearch, dl);
-
- while(curCount < theindex && next != NULL) {
- curCount++;
- last = next + 1;
- next = strchr(last, dl);
- }
-
- if ((curCount < theindex) || (next == NULL))
- toReturn = strlen(toSearch) - (curCount - 1);
- else
- toReturn = next - toSearch - (curCount - 1);
-
- return toReturn;
-}
-
-int
-aimutil_itemcnt(char *toSearch, char dl)
-{
- int curCount;
- char *next;
-
- curCount = 1;
-
- next = strchr(toSearch, dl);
-
- while(next != NULL) {
- curCount++;
- next = strchr(next + 1, dl);
- }
-
- return curCount;
-}
-
-char *
-aimutil_itemindex(char *toSearch, int theindex, char dl)
-{
- int curCount;
- char *next;
- char *last;
- char *toReturn;
-
- curCount = 0;
-
- last = toSearch;
- next = strchr(toSearch, dl);
-
- while (curCount < theindex && next != NULL) {
- curCount++;
- last = next + 1;
- next = strchr(last, dl);
- }
- next = strchr(last, dl);
-
- if (curCount < theindex) {
- toReturn = g_malloc(sizeof(char));
- *toReturn = '\0';
- } else {
- if (next == NULL) {
- toReturn = g_malloc((strlen(last) + 1) * sizeof(char));
- strcpy(toReturn, last);
- } else {
- toReturn = g_malloc((next - last + 1) * sizeof(char));
- memcpy(toReturn, last, (next - last));
- toReturn[next - last] = '\0';
- }
- }
- return toReturn;
-}
-
/**
* Calculate the checksum of a given icon.
*/
@@ -289,3 +238,89 @@ oscar_util_name_compare(const char *name1, const char *name2)
return 0;
}
+
+/**
+ * Looks for %n, %d, or %t in a string, and replaces them with the
+ * specified name, date, and time, respectively.
+ *
+ * @param str The string that may contain the special variables.
+ * @param name The sender name.
+ *
+ * @return A newly allocated string where the special variables are
+ * expanded. This should be g_free'd by the caller.
+ */
+gchar *
+oscar_util_format_string(const char *str, const char *name)
+{
+ char *c;
+ GString *cpy;
+ time_t t;
+ struct tm *tme;
+
+ g_return_val_if_fail(str != NULL, NULL);
+ g_return_val_if_fail(name != NULL, NULL);
+
+ /* Create an empty GString that is hopefully big enough for most messages */
+ cpy = g_string_sized_new(1024);
+
+ t = time(NULL);
+ tme = localtime(&t);
+
+ c = (char *)str;
+ while (*c) {
+ switch (*c) {
+ case '%':
+ if (*(c + 1)) {
+ switch (*(c + 1)) {
+ case 'n':
+ /* append name */
+ g_string_append(cpy, name);
+ c++;
+ break;
+ case 'd':
+ /* append date */
+ g_string_append(cpy, purple_date_format_short(tme));
+ c++;
+ break;
+ case 't':
+ /* append time */
+ g_string_append(cpy, purple_time_format(tme));
+ c++;
+ break;
+ default:
+ g_string_append_c(cpy, *c);
+ }
+ } else {
+ g_string_append_c(cpy, *c);
+ }
+ break;
+ default:
+ g_string_append_c(cpy, *c);
+ }
+ c++;
+ }
+
+ return g_string_free(cpy, FALSE);
+}
+
+gchar *
+oscar_format_buddies(GSList *buddies, const gchar *no_buddies_message)
+{
+ GSList *cur;
+ GString *result;
+ if (!buddies) {
+ return g_strdup_printf("<i>%s</i>", no_buddies_message);
+ }
+ result = g_string_new("");
+ for (cur = buddies; cur != NULL; cur = cur->next) {
+ PurpleBuddy *buddy = cur->data;
+ const gchar *bname = purple_buddy_get_name(buddy);
+ const gchar *alias = purple_buddy_get_alias_only(buddy);
+ g_string_append(result, bname);
+ if (alias) {
+ g_string_append_printf(result, " (%s)", alias);
+ }
+ g_string_append(result, "<br>");
+ }
+ return g_string_free(result, FALSE);
+}
diff --git a/libpurple/protocols/oscar/visibility.c b/libpurple/protocols/oscar/visibility.c
new file mode 100644
index 0000000000..abec4688f8
--- /dev/null
+++ b/libpurple/protocols/oscar/visibility.c
@@ -0,0 +1,199 @@
+/*
+ * Purple's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
+ */
+
+#include "visibility.h"
+#include "request.h"
+
+/* 4 separate strings are needed in order to ease translators' job */
+#define APPEAR_ONLINE N_("Appear Online")
+#define DONT_APPEAR_ONLINE N_("Don't Appear Online")
+#define APPEAR_OFFLINE N_("Appear Offline")
+#define DONT_APPEAR_OFFLINE N_("Don't Appear Offline")
+
+static guint16
+get_buddy_list_type(OscarData *od)
+{
+ PurpleAccount *account = purple_connection_get_account(od->gc);
+ return purple_account_is_status_active(account, OSCAR_STATUS_ID_INVISIBLE) ? AIM_SSI_TYPE_PERMIT : AIM_SSI_TYPE_DENY;
+}
+
+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;
+}
+
+static void
+visibility_cb(PurpleBlistNode *node, gpointer whatever)
+{
+ PurpleBuddy *buddy = PURPLE_BUDDY(node);
+ const char* bname = purple_buddy_get_name(buddy);
+ OscarData *od = purple_connection_get_protocol_data(purple_account_get_connection(purple_buddy_get_account(buddy)));
+ guint16 list_type = get_buddy_list_type(od);
+
+ if (!is_buddy_on_list(od, bname)) {
+ aim_ssi_add_to_private_list(od, bname, list_type);
+ } else {
+ aim_ssi_del_from_private_list(od, bname, list_type);
+ }
+}
+
+PurpleMenuAction *
+create_visibility_menu_item(OscarData *od, const char *bname)
+{
+ PurpleAccount *account = purple_connection_get_account(od->gc);
+ gboolean invisible = purple_account_is_status_active(account, OSCAR_STATUS_ID_INVISIBLE);
+ gboolean on_list = is_buddy_on_list(od, bname);
+ const gchar *label;
+
+ if (invisible) {
+ label = on_list ? _(DONT_APPEAR_ONLINE) : _(APPEAR_ONLINE);
+ } else {
+ label = on_list ? _(DONT_APPEAR_OFFLINE) : _(APPEAR_OFFLINE);
+ }
+ return purple_menu_action_new(label, PURPLE_CALLBACK(visibility_cb), NULL, NULL);
+}
+
+typedef void (*ShowDialog)(PurplePluginAction *);
+
+struct list_remove_data
+{
+ PurplePluginAction *action;
+ ShowDialog show_dialog_again;
+ OscarData *od;
+ guint16 list_type;
+ const gchar *list_name;
+};
+
+static void
+list_remove_cb(struct list_remove_data *data, PurpleRequestFields *fields)
+{
+ ShowDialog show_dialog_again = data->show_dialog_again;
+ PurplePluginAction *action = data->action;
+ PurpleRequestField *field = purple_request_fields_get_field(fields, "list-items");
+ GList *sels = purple_request_field_list_get_selected(field);
+ for (; sels; sels = sels->next) {
+ const gchar *name = sels->data;
+ const gchar *bname = purple_request_field_list_get_data(field, name);
+
+ purple_debug_info("oscar", "Removing %s from %s\n", bname, data->list_name);
+
+ aim_ssi_del_from_private_list(data->od, bname, data->list_type);
+ }
+
+ g_free(data);
+ show_dialog_again(action);
+}
+
+static void
+list_close_cb(struct list_remove_data *data, gpointer ignored)
+{
+ g_free(data);
+}
+
+static void
+show_private_list(PurplePluginAction *action,
+ guint16 list_type,
+ const gchar *list_name,
+ const gchar *list_description,
+ const gchar *menu_action_name,
+ ShowDialog caller)
+{
+ PurpleConnection *gc = (PurpleConnection *) action->context;
+ OscarData *od = purple_connection_get_protocol_data(gc);
+ PurpleAccount *account = purple_connection_get_account(gc);
+ GSList *buddies, *cur;
+ gchar *desc;
+ struct list_remove_data *data;
+
+ PurpleRequestField *field;
+ PurpleRequestFields *fields = purple_request_fields_new();
+ PurpleRequestFieldGroup *group = purple_request_field_group_new(NULL);
+
+ purple_request_fields_add_group(fields, group);
+
+ desc = g_strdup_printf(_("You can add a buddy to this list "
+ "by right-clicking on them and "
+ "selecting \"%s\""), menu_action_name);
+
+ field = purple_request_field_list_new("list-items", desc);
+ g_free(desc);
+
+ purple_request_field_group_add_field(group, field);
+
+ purple_request_field_list_set_multi_select(field, TRUE);
+ purple_request_field_set_required(field, TRUE);
+
+ buddies = purple_find_buddies(account, NULL);
+ for (cur = buddies; cur != NULL; cur = cur->next) {
+ PurpleBuddy *buddy;
+ const gchar *bname;
+
+ buddy = cur->data;
+ bname = purple_buddy_get_name(buddy);
+ if (aim_ssi_itemlist_finditem(od->ssi.local, NULL, bname, list_type)) {
+ const gchar *alias = purple_buddy_get_alias_only(buddy);
+ char *dname = alias ? g_strdup_printf("%s (%s)", bname, alias) : NULL;
+ purple_request_field_list_add(field, dname ? dname : bname, (void *)bname);
+ g_free(dname);
+ }
+ }
+
+ g_slist_free(buddies);
+
+ data = g_new0(struct list_remove_data, 1);
+ data->action = action;
+ data->show_dialog_again = caller;
+ data->od = od;
+ data->list_type = list_type;
+ data->list_name = list_name;
+
+ purple_request_fields(gc, list_name, list_description, NULL,
+ fields,
+ _("Close"), G_CALLBACK(list_close_cb),
+ _("Remove"), G_CALLBACK(list_remove_cb),
+ account, NULL, NULL,
+ data);
+}
+
+void
+oscar_show_visible_list(PurplePluginAction *action)
+{
+ show_private_list(action,
+ AIM_SSI_TYPE_PERMIT,
+ _("Visible List"),
+ _("These buddies will see "
+ "your status when you switch "
+ "to \"Invisible\""),
+ _(APPEAR_ONLINE),
+ oscar_show_visible_list);
+}
+
+void
+oscar_show_invisible_list(PurplePluginAction *action)
+{
+ show_private_list(action,
+ AIM_SSI_TYPE_DENY,
+ _("Invisible List"),
+ _("These buddies will always see you as offline"),
+ _(APPEAR_OFFLINE),
+ oscar_show_invisible_list);
+}
+
diff --git a/libpurple/protocols/oscar/family_translate.c b/libpurple/protocols/oscar/visibility.h
index 005528f116..77ea4c2c30 100644
--- a/libpurple/protocols/oscar/family_translate.c
+++ b/libpurple/protocols/oscar/visibility.h
@@ -18,29 +18,15 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-/*
- * Family 0x000c - Translation.
- *
- * I have no idea why this group was issued. I have never seen anything
- * that uses it. From what I remember, the last time I tried to poke at
- * the server with this group, it whined about not supporting it.
- *
- * But we advertise it anyway, because its fun.
- *
- */
+#ifndef _VISIBILITY_H_
+#define _VISIBILITY_H_
#include "oscar.h"
+#include "plugin.h"
+#include "util.h"
-int translate_modfirst(OscarData *od, aim_module_t *mod)
-{
-
- mod->family = SNAC_FAMILY_TRANSLATE;
- mod->version = 0x0001;
- mod->toolid = 0x0104;
- mod->toolversion = 0x0001;
- mod->flags = 0;
- strncpy(mod->name, "translate", sizeof(mod->name));
- mod->snachandler = NULL;
+PurpleMenuAction * create_visibility_menu_item(OscarData *od, const char *bname);
+void oscar_show_visible_list(PurplePluginAction *action);
+void oscar_show_invisible_list(PurplePluginAction *action);
- return 0;
-}
+#endif \ No newline at end of file
diff --git a/libpurple/protocols/qq/ChangeLog b/libpurple/protocols/qq/ChangeLog
index cb6d7a1877..b0a7534071 100644
--- a/libpurple/protocols/qq/ChangeLog
+++ b/libpurple/protocols/qq/ChangeLog
@@ -233,7 +233,7 @@
2008.08.06 - ccpaging <ccpaging(at)gmail.com>
* Rename names of variables, Group, to Room
* Functions of group_network merged into qq_network and qq_process
- * Canceled managing glist of group packet, add sub_cmdd and room_id to transaction
+ * Cancelled managing glist of group packet, add sub_cmdd and room_id to transaction
* Fixed error of demo group:
If 'room list' and 'room infor' are not setup, response received from server will emits 'room_id = 0' packet.
diff --git a/libpurple/protocols/qq/buddy_info.c b/libpurple/protocols/qq/buddy_info.c
index 019ac8e41c..489bd1c525 100644
--- a/libpurple/protocols/qq/buddy_info.c
+++ b/libpurple/protocols/qq/buddy_info.c
@@ -224,12 +224,10 @@ static void info_display_only(PurpleConnection *gc, gchar **segments)
void qq_request_buddy_info(PurpleConnection *gc, guint32 uid,
guint32 update_class, int action)
{
- qq_data *qd;
gchar raw_data[16] = {0};
g_return_if_fail(uid != 0);
- qd = (qq_data *) gc->proto_data;
g_snprintf(raw_data, sizeof(raw_data), "%u", uid);
qq_send_cmd_mess(gc, QQ_CMD_GET_BUDDY_INFO, (guint8 *) raw_data, strlen(raw_data),
update_class, action);
@@ -271,7 +269,6 @@ static void info_modify_cancel_cb(modify_info_request *info_request)
static void info_modify_ok_cb(modify_info_request *info_request, PurpleRequestFields *fields)
{
PurpleConnection *gc;
- qq_data *qd;
gchar **segments;
int index;
const char *utf8_str;
@@ -279,8 +276,7 @@ static void info_modify_ok_cb(modify_info_request *info_request, PurpleRequestFi
int choice_num;
gc = info_request->gc;
- g_return_if_fail(gc != NULL && info_request->gc);
- qd = (qq_data *) gc->proto_data;
+ g_return_if_fail(gc != NULL);
segments = info_request->segments;
g_return_if_fail(segments != NULL);
@@ -390,14 +386,12 @@ static void field_request_new(PurpleRequestFieldGroup *group, gint index, gchar
static void info_modify_dialogue(PurpleConnection *gc, gchar **segments, int iclass)
{
- qq_data *qd;
PurpleRequestFieldGroup *group;
PurpleRequestFields *fields;
modify_info_request *info_request;
gchar *utf8_title, *utf8_prim;
int index;
- qd = (qq_data *) gc->proto_data;
/* Keep one dialog once a time */
purple_request_close_with_handle(gc);
@@ -416,9 +410,11 @@ static void info_modify_dialogue(PurpleConnection *gc, gchar **segments, int icl
case QQ_FIELD_CONTACT:
utf8_title = g_strdup(_("Modify Contact"));
utf8_prim = g_strdup_printf("%s for %s", _("Modify Contact"), segments[0]);
+ break;
case QQ_FIELD_ADDR:
utf8_title = g_strdup(_("Modify Address"));
utf8_prim = g_strdup_printf("%s for %s", _("Modify Address"), segments[0]);
+ break;
case QQ_FIELD_EXT:
utf8_title = g_strdup(_("Modify Extended Information"));
utf8_prim = g_strdup_printf("%s for %s", _("Modify Extended Information"), segments[0]);
@@ -427,6 +423,7 @@ static void info_modify_dialogue(PurpleConnection *gc, gchar **segments, int icl
default:
utf8_title = g_strdup(_("Modify Information"));
utf8_prim = g_strdup_printf("%s for %s", _("Modify Information"), segments[0]);
+ break;
}
info_request = g_new0(modify_info_request, 1);
diff --git a/libpurple/protocols/qq/buddy_list.c b/libpurple/protocols/qq/buddy_list.c
index 7498e1a54f..a927a87f3a 100644
--- a/libpurple/protocols/qq/buddy_list.c
+++ b/libpurple/protocols/qq/buddy_list.c
@@ -55,11 +55,9 @@ typedef struct _qq_buddy_online {
/* get a list of online_buddies */
void qq_request_get_buddies_online(PurpleConnection *gc, guint8 position, guint32 update_class)
{
- qq_data *qd;
guint8 *raw_data;
gint bytes = 0;
- qd = (qq_data *) gc->proto_data;
raw_data = g_newa(guint8, 5);
/* 000-000 get online friends cmd
@@ -360,7 +358,6 @@ guint16 qq_process_get_buddies(guint8 *data, gint data_len, PurpleConnection *gc
guint32 qq_process_get_buddies_and_rooms(guint8 *data, gint data_len, PurpleConnection *gc)
{
- qq_data *qd;
gint i, j;
gint bytes;
guint8 sub_cmd, reply_code;
@@ -371,8 +368,6 @@ guint32 qq_process_get_buddies_and_rooms(guint8 *data, gint data_len, PurpleConn
g_return_val_if_fail(data != NULL && data_len != 0, -1);
- qd = (qq_data *) gc->proto_data;
-
bytes = 0;
bytes += qq_get8(&sub_cmd, data + bytes);
g_return_val_if_fail(sub_cmd == 0x01, -1);
@@ -468,11 +463,6 @@ void qq_request_change_status(PurpleConnection *gc, guint32 update_class)
guint8 away_cmd;
guint32 misc_status;
gboolean fake_video;
- PurpleAccount *account;
- PurplePresence *presence;
-
- account = purple_connection_get_account(gc);
- presence = purple_account_get_presence(account);
qd = (qq_data *) gc->proto_data;
if (!qd->is_login)
@@ -596,14 +586,13 @@ void qq_process_buddy_change_status(guint8 *data, gint data_len, PurpleConnectio
void qq_update_buddy_status(PurpleConnection *gc, guint32 uid, guint8 status, guint8 flag)
{
gchar *who;
- gchar *status_id;
+ const gchar *status_id;
g_return_if_fail(uid != 0);
/* purple supports signon and idle time
* but it is not much use for QQ, I do not use them */
/* serv_got_update(gc, name, online, 0, q_bud->signon, q_bud->idle, bud->uc); */
- status_id = "available";
switch(status) {
case QQ_BUDDY_OFFLINE:
status_id = "offline";
@@ -677,13 +666,10 @@ void qq_update_buddyies_status(PurpleConnection *gc)
void qq_buddy_data_free_all(PurpleConnection *gc)
{
- qq_data *qd;
PurpleBuddy *buddy;
GSList *buddies, *it;
gint count = 0;
- qd = (qq_data *)purple_connection_get_protocol_data(gc);
-
buddies = purple_find_buddies(purple_connection_get_account(gc), NULL);
for (it = buddies; it; it = it->next) {
qq_buddy_data *qbd = NULL;
diff --git a/libpurple/protocols/qq/buddy_opt.c b/libpurple/protocols/qq/buddy_opt.c
index f8bb588367..fef9ee3920 100644
--- a/libpurple/protocols/qq/buddy_opt.c
+++ b/libpurple/protocols/qq/buddy_opt.c
@@ -262,7 +262,6 @@ void qq_request_auth_code(PurpleConnection *gc, guint8 cmd, guint16 sub_cmd, gui
void qq_process_auth_code(PurpleConnection *gc, guint8 *data, gint data_len, guint32 uid)
{
- qq_data *qd;
gint bytes;
guint8 cmd, reply;
guint16 sub_cmd;
@@ -272,8 +271,6 @@ void qq_process_auth_code(PurpleConnection *gc, guint8 *data, gint data_len, gui
g_return_if_fail(data != NULL && data_len != 0);
g_return_if_fail(uid != 0);
- qd = (qq_data *) gc->proto_data;
-
qq_show_packet("qq_process_auth_code", data, data_len);
bytes = 0;
bytes += qq_get8(&cmd, data + bytes);
@@ -324,7 +321,7 @@ static void add_buddy_question_input(PurpleConnection *gc, guint32 uid, gchar *q
add_req->auth_len = 0;
who = uid_to_purple_name(uid);
- msg = g_strdup_printf(_("%u requires verification"), uid);
+ msg = g_strdup_printf(_("%u requires verification: %s"), uid, question);
purple_request_input(gc, _("Add buddy question"), msg,
_("Enter answer here"),
NULL,
@@ -400,7 +397,6 @@ static void request_add_buddy_by_question(PurpleConnection *gc, guint32 uid,
void qq_process_question(PurpleConnection *gc, guint8 *data, gint data_len, guint32 uid)
{
- qq_data *qd;
gint bytes;
guint8 cmd, reply;
gchar *question, *answer;
@@ -409,8 +405,6 @@ void qq_process_question(PurpleConnection *gc, guint8 *data, gint data_len, guin
g_return_if_fail(data != NULL && data_len != 0);
- qd = (qq_data *) gc->proto_data;
-
qq_show_packet("qq_process_question", data, data_len);
bytes = 0;
bytes += qq_get8(&cmd, data + bytes);
@@ -720,13 +714,10 @@ void qq_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
/* process reply to add_buddy_auth request */
void qq_process_add_buddy_auth(guint8 *data, gint data_len, PurpleConnection *gc)
{
- qq_data *qd;
gchar **segments, *msg_utf8;
g_return_if_fail(data != NULL && data_len != 0);
- qd = (qq_data *) gc->proto_data;
-
if (data[0] == '0') {
purple_debug_info("QQ", "Reply OK for sending authorize\n");
return;
@@ -767,11 +758,9 @@ void qq_process_remove_buddy(PurpleConnection *gc, guint8 *data, gint data_len,
/* process the server reply for my request to remove myself from a buddy */
void qq_process_buddy_remove_me(PurpleConnection *gc, guint8 *data, gint data_len, guint32 uid)
{
- qq_data *qd;
gchar *msg;
g_return_if_fail(data != NULL && data_len != 0);
- qd = (qq_data *) gc->proto_data;
if (data[0] == 0) {
purple_debug_info("QQ", "Reply OK for removing me from %u's buddy list\n", uid);
@@ -1004,7 +993,6 @@ static void server_buddy_add_request(PurpleConnection *gc, gchar *from, gchar *t
void qq_process_buddy_check_code(PurpleConnection *gc, guint8 *data, gint data_len)
{
- qq_data *qd;
gint bytes;
guint8 cmd;
guint8 reply;
@@ -1013,8 +1001,6 @@ void qq_process_buddy_check_code(PurpleConnection *gc, guint8 *data, gint data_l
g_return_if_fail(data != NULL && data_len >= 5);
- qd = (qq_data *) gc->proto_data;
-
qq_show_packet("buddy_check_code", data, data_len);
bytes = 0;
diff --git a/libpurple/protocols/qq/char_conv.c b/libpurple/protocols/qq/char_conv.c
index 3db2c0caf3..0dfedc1306 100644
--- a/libpurple/protocols/qq/char_conv.c
+++ b/libpurple/protocols/qq/char_conv.c
@@ -37,7 +37,7 @@
/* convert a string from from_charset to to_charset, using g_convert */
/* Warning: do not return NULL */
-static gchar *do_convert(const gchar *str, gssize len, const gchar *to_charset, const gchar *from_charset)
+static gchar *do_convert(const gchar *str, gssize len, guint8 *out_len, const gchar *to_charset, const gchar *from_charset)
{
GError *error = NULL;
gchar *ret;
@@ -48,6 +48,8 @@ static gchar *do_convert(const gchar *str, gssize len, const gchar *to_charset,
ret = g_convert(str, len, to_charset, from_charset, &byte_read, &byte_write, &error);
if (error == NULL) {
+ if (out_len)
+ *out_len = byte_write;
return ret; /* convert is OK */
}
@@ -67,7 +69,8 @@ static gchar *do_convert(const gchar *str, gssize len, const gchar *to_charset,
*/
gint qq_get_vstr(gchar **ret, const gchar *from_charset, guint8 *data)
{
- guint8 len;
+ gssize len;
+ guint8 out_len;
g_return_val_if_fail(data != NULL && from_charset != NULL, -1);
@@ -76,9 +79,9 @@ gint qq_get_vstr(gchar **ret, const gchar *from_charset, guint8 *data)
*ret = g_strdup("");
return 1;
}
- *ret = do_convert((gchar *) (data + 1), (gssize) len, UTF8, from_charset);
+ *ret = do_convert((gchar *) (data + 1), len, &out_len, UTF8, from_charset);
- return len + 1;
+ return out_len + 1;
}
gint qq_put_vstr(guint8 *buf, const gchar *str_utf8, const gchar *to_charset)
@@ -86,12 +89,11 @@ gint qq_put_vstr(guint8 *buf, const gchar *str_utf8, const gchar *to_charset)
gchar *str;
guint8 len;
- if (str_utf8 == NULL || (len = strlen(str_utf8)) == 0) {
+ if (str_utf8 == NULL || str_utf8[0] == '\0') {
buf[0] = 0;
return 1;
}
- str = do_convert(str_utf8, -1, to_charset, UTF8);
- len = strlen(str_utf8);
+ str = do_convert(str_utf8, -1, &len, to_charset, UTF8);
buf[0] = len;
if (len > 0) {
memcpy(buf + 1, str, len);
@@ -102,12 +104,12 @@ gint qq_put_vstr(guint8 *buf, const gchar *str_utf8, const gchar *to_charset)
/* Warning: do not return NULL */
gchar *utf8_to_qq(const gchar *str, const gchar *to_charset)
{
- return do_convert(str, -1, to_charset, UTF8);
+ return do_convert(str, -1, NULL, to_charset, UTF8);
}
/* Warning: do not return NULL */
gchar *qq_to_utf8(const gchar *str, const gchar *from_charset)
{
- return do_convert(str, -1, UTF8, from_charset);
+ return do_convert(str, -1, NULL, UTF8, from_charset);
}
diff --git a/libpurple/protocols/qq/file_trans.c b/libpurple/protocols/qq/file_trans.c
index 1ee9a3172c..a0bb67072a 100644
--- a/libpurple/protocols/qq/file_trans.c
+++ b/libpurple/protocols/qq/file_trans.c
@@ -238,12 +238,9 @@ static gint _qq_send_file(PurpleConnection *gc, guint8 *data, gint len, guint16
gint bytes = 0;
guint32 file_key;
qq_data *qd;
- ft_info *info;
qd = (qq_data *) gc->proto_data;
- info = (ft_info *) qd->xfer->data;
-
raw_data = g_newa(guint8, MAX_PACKET_SIZE);
file_key = _gen_file_key();
@@ -805,9 +802,6 @@ void qq_process_recv_file(PurpleConnection *gc, guint8 *data, gint len)
{
gint bytes;
guint8 tag;
- qq_data *qd;
-
- qd = (qq_data *) gc->proto_data;
bytes = 0;
bytes += qq_get8(&tag, data + bytes);
diff --git a/libpurple/protocols/qq/group.c b/libpurple/protocols/qq/group.c
index fe0fe22bff..268bda23a6 100644
--- a/libpurple/protocols/qq/group.c
+++ b/libpurple/protocols/qq/group.c
@@ -119,13 +119,11 @@ PurpleRoomlist *qq_roomlist_get_list(PurpleConnection *gc)
/* free roomlist space, I have no idea when this one is called... */
void qq_roomlist_cancel(PurpleRoomlist *list)
{
- qq_data *qd;
PurpleConnection *gc;
g_return_if_fail(list != NULL);
gc = purple_account_get_connection(list->account);
- qd = (qq_data *) gc->proto_data;
purple_roomlist_set_in_progress(list, FALSE);
purple_roomlist_unref(list);
}
diff --git a/libpurple/protocols/qq/group_im.c b/libpurple/protocols/qq/group_im.c
index cba79accf5..2816e124c7 100644
--- a/libpurple/protocols/qq/group_im.c
+++ b/libpurple/protocols/qq/group_im.c
@@ -48,12 +48,10 @@
PurpleConversation *qq_room_conv_open(PurpleConnection *gc, qq_room_data *rmd)
{
PurpleConversation *conv;
- qq_data *qd;
gchar *topic_utf8;
g_return_val_if_fail(rmd != NULL, NULL);
g_return_val_if_fail(rmd->title_utf8, NULL);
- qd = (qq_data *) gc->proto_data;
conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
rmd->title_utf8, purple_connection_get_account(gc));
@@ -207,7 +205,6 @@ void qq_room_got_chat_in(PurpleConnection *gc,
/* recv an IM from a group chat */
void qq_process_room_im(guint8 *data, gint data_len, guint32 id, PurpleConnection *gc, guint16 msg_type)
{
- qq_data *qd;
gchar *msg_smiley, *msg_fmt, *msg_utf8;
gint bytes, tail_len;
struct {
@@ -229,7 +226,6 @@ void qq_process_room_im(guint8 *data, gint data_len, guint32 id, PurpleConnectio
/* at least include im_text.msg_len */
g_return_if_fail(data != NULL && data_len > 23);
- qd = (qq_data *) gc->proto_data;
/* qq_show_packet("ROOM_IM", data, data_len); */
memset(&im_text, 0, sizeof(im_text));
@@ -376,7 +372,6 @@ int qq_chat_send(PurpleConnection *gc, int id, const char *what, PurpleMessageFl
gint msg_len;
const gchar *start_invalid;
gboolean is_smiley_none;
- guint8 frag_count, frag_index;
g_return_val_if_fail(NULL != gc && NULL != gc->proto_data, -1);
g_return_val_if_fail(id != 0 && what != NULL, -1);
@@ -386,9 +381,6 @@ int qq_chat_send(PurpleConnection *gc, int id, const char *what, PurpleMessageFl
/* qq_show_packet("chat IM UTF8", (guint8 *)what, strlen(what)); */
- fmt = qq_im_fmt_new_by_purple(what);
- is_smiley_none = qq_im_smiley_none(what);
-
msg_stripped = purple_markup_strip_html(what);
g_return_val_if_fail(msg_stripped != NULL, -1);
/* qq_show_packet("IM Stripped", (guint8 *)what, strlen(what)); */
@@ -417,26 +409,10 @@ int qq_chat_send(PurpleConnection *gc, int id, const char *what, PurpleMessageFl
qd->send_im_id++;
fmt = qq_im_fmt_new_by_purple(what);
- frag_count = g_slist_length(segments);
- frag_index = 0;
-/*
- if (frag_count <= 1) {
-*/
- for (it = segments; it; it = it->next) {
- request_room_send_im(gc, id, fmt, (gchar *)it->data);
- g_free(it->data);
- }
-/*
- } else {
- for (it = segments; it; it = it->next) {
- request_room_send_im_ex(gc, id, fmt, (gchar *)it->data,
- qd->send_im_id, frag_count, frag_index);
- g_free(it->data);
- frag_index++;
- }
+ for (it = segments; it; it = g_slist_delete_link(it, it)) {
+ request_room_send_im(gc, id, fmt, (gchar *)it->data);
+ g_free(it->data);
}
-*/
qq_im_fmt_free(fmt);
- g_slist_free(segments);
return 1;
}
diff --git a/libpurple/protocols/qq/group_join.c b/libpurple/protocols/qq/group_join.c
index 8bf3df090b..6d27a2ff05 100644
--- a/libpurple/protocols/qq/group_join.c
+++ b/libpurple/protocols/qq/group_join.c
@@ -178,12 +178,10 @@ void qq_send_cmd_group_auth(PurpleConnection *gc, qq_room_data *rmd,
/* If comes here, cmd is OK already */
void qq_process_group_cmd_exit_group(guint8 *data, gint len, PurpleConnection *gc)
{
- qq_data *qd;
gint bytes;
guint32 id;
g_return_if_fail(data != NULL && len > 0);
- qd = (qq_data *) gc->proto_data;
if (len < 4) {
purple_debug_error("QQ", "Invalid exit group reply, expect %d bytes, read %d bytes\n", 4, len);
@@ -201,12 +199,10 @@ void qq_process_group_cmd_join_group_auth(guint8 *data, gint len, PurpleConnecti
{
gint bytes;
guint32 id;
- qq_data *qd;
qq_room_data *rmd;
gchar *msg;
g_return_if_fail(data != NULL && len > 0);
- qd = (qq_data *) gc->proto_data;
if (len < 4) {
purple_debug_error("QQ",
@@ -283,7 +279,6 @@ void qq_process_group_cmd_join_group(guint8 *data, gint len, PurpleConnection *g
/* Attempt to join a group without auth */
void qq_group_join(PurpleConnection *gc, GHashTable *data)
{
- qq_data *qd;
gchar *ext_id_str;
gchar *id_str;
guint32 ext_id;
@@ -291,7 +286,6 @@ void qq_group_join(PurpleConnection *gc, GHashTable *data)
qq_room_data *rmd;
g_return_if_fail(data != NULL);
- qd = (qq_data *) gc->proto_data;
ext_id_str = g_hash_table_lookup(data, QQ_ROOM_KEY_EXTERNAL_ID);
id_str = g_hash_table_lookup(data, QQ_ROOM_KEY_INTERNAL_ID);
diff --git a/libpurple/protocols/qq/group_opt.c b/libpurple/protocols/qq/group_opt.c
index b30608c2e0..5391f0fc87 100644
--- a/libpurple/protocols/qq/group_opt.c
+++ b/libpurple/protocols/qq/group_opt.c
@@ -134,12 +134,10 @@ void qq_group_modify_members(PurpleConnection *gc, qq_room_data *rmd, guint32 *n
{
guint32 *old_members, *del_members, *add_members;
qq_buddy_data *bd;
- qq_data *qd;
gint i = 0, old = 0, new = 0, del = 0, add = 0;
GList *list;
g_return_if_fail(rmd != NULL);
- qd = (qq_data *) gc->proto_data;
if (new_members[0] == 0xffffffff)
return;
diff --git a/libpurple/protocols/qq/im.c b/libpurple/protocols/qq/im.c
index c4363bf826..1f36fa8a1b 100644
--- a/libpurple/protocols/qq/im.c
+++ b/libpurple/protocols/qq/im.c
@@ -725,7 +725,6 @@ void qq_got_message(PurpleConnection *gc, const gchar *msg)
/* process received normal text IM */
static void process_im_text(PurpleConnection *gc, guint8 *data, gint len, qq_im_header *im_header)
{
- qq_data *qd;
guint16 purple_msg_type;
gchar *who;
gchar *msg_smiley, *msg_fmt, *msg_utf8;
@@ -749,10 +748,9 @@ static void process_im_text(PurpleConnection *gc, guint8 *data, gint len, qq_im_
gchar *msg; /* no fixed length, ends with 0x00 */
} im_text;
- g_return_if_fail (data != NULL && len > 0);
+ g_return_if_fail(data != NULL && len > 0);
g_return_if_fail(im_header != NULL);
- qd = (qq_data *) gc->proto_data;
memset(&im_text, 0, sizeof(im_text));
/* qq_show_packet("IM text", data, len); */
@@ -823,7 +821,6 @@ static void process_im_text(PurpleConnection *gc, guint8 *data, gint len, qq_im_
/* process received extended (2007) text IM */
static void process_extend_im_text(PurpleConnection *gc, guint8 *data, gint len, qq_im_header *im_header)
{
- qq_data *qd;
guint16 purple_msg_type;
gchar *who;
gchar *msg_smiley, *msg_fmt, *msg_utf8;
@@ -848,10 +845,9 @@ static void process_extend_im_text(PurpleConnection *gc, guint8 *data, gint len,
guint8 fromMobileQQ;
} im_text;
- g_return_if_fail (data != NULL && len > 0);
+ g_return_if_fail(data != NULL && len > 0);
g_return_if_fail(im_header != NULL);
- qd = (qq_data *) gc->proto_data;
memset(&im_text, 0, sizeof(im_text));
/* qq_show_packet("Extend IM text", data, len); */
@@ -1043,12 +1039,10 @@ static void request_send_im(PurpleConnection *gc, guint32 uid_to, gint type,
{
qq_data *qd;
guint8 raw_data[MAX_PACKET_SIZE - 16];
- guint16 im_type;
gint bytes;
time_t now;
qd = (qq_data *) gc->proto_data;
- im_type = QQ_NORMAL_IM_TEXT;
/* purple_debug_info("QQ", "Send IM %d-%d\n", frag_count, frag_index); */
bytes = 0;
@@ -1118,13 +1112,12 @@ GSList *qq_im_get_segments(gchar *msg_stripped, gboolean is_smiley_none)
GString *new_string;
GString *append_utf8;
gchar *start, *p;
- gint count, len;
+ gint len;
qq_emoticon *emoticon;
g_return_val_if_fail(msg_stripped != NULL, NULL);
start = msg_stripped;
- count = 0;
new_string = g_string_new("");
append_utf8 = g_string_new("");
while (*start) {
diff --git a/libpurple/protocols/qq/qq.c b/libpurple/protocols/qq/qq.c
index 44d10c5b77..46cbc8d8ba 100644
--- a/libpurple/protocols/qq/qq.c
+++ b/libpurple/protocols/qq/qq.c
@@ -89,15 +89,12 @@ static void server_list_create(PurpleAccount *account)
{
PurpleConnection *gc;
qq_data *qd;
- PurpleProxyInfo *gpi;
const gchar *custom_server;
gc = purple_account_get_connection(account);
g_return_if_fail(gc != NULL && gc->proto_data != NULL);
qd = gc->proto_data;
- gpi = purple_proxy_get_setup(account);
-
qd->use_tcp = purple_account_get_bool(account, "use_tcp", TRUE);
custom_server = purple_account_get_string(account, "server", NULL);
@@ -381,13 +378,10 @@ static void qq_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gbo
static const char *qq_list_emblem(PurpleBuddy *b)
{
PurpleAccount *account;
- PurpleConnection *gc;
- qq_data *qd;
qq_buddy_data *buddy;
if (!b || !(account = purple_buddy_get_account(b)) ||
- !(gc = purple_account_get_connection(account)) ||
- !(qd = purple_connection_get_protocol_data(gc)))
+ !purple_account_get_connection(account))
return NULL;
buddy = purple_buddy_get_protocol_data(b);
@@ -620,12 +614,10 @@ static void action_show_account_info(PurplePluginAction *action)
static void action_about_openq(PurplePluginAction *action)
{
PurpleConnection *gc = (PurpleConnection *) action->context;
- qq_data *qd;
GString *info;
gchar *title;
- g_return_if_fail(NULL != gc && NULL != gc->proto_data);
- qd = (qq_data *) gc->proto_data;
+ g_return_if_fail(NULL != gc);
info = g_string_new("<html><body>");
g_string_append(info, _("<p><b>Original Author</b>:<br>\n"));
@@ -1041,7 +1033,9 @@ static PurplePluginProtocolInfo prpl_info =
NULL, /* get_account_text_table */
NULL, /* initiate_media */
NULL, /* get_media_caps */
- NULL /* get_moods */
+ NULL, /* get_moods */
+ NULL, /* set_public_alias */
+ NULL /* get_public_alias */
};
static PurplePluginInfo info = {
diff --git a/libpurple/protocols/qq/qq_base.c b/libpurple/protocols/qq/qq_base.c
index 34ba975dfa..281d1b451e 100644
--- a/libpurple/protocols/qq/qq_base.c
+++ b/libpurple/protocols/qq/qq_base.c
@@ -386,7 +386,6 @@ static const guint8 login_53_68[16] = {
/* process the login reply packet */
guint8 qq_process_login( PurpleConnection *gc, guint8 *data, gint data_len)
{
- qq_data *qd;
guint8 ret = data[0];
gchar *msg, *msg_utf8;
gchar *error;
@@ -394,8 +393,6 @@ guint8 qq_process_login( PurpleConnection *gc, guint8 *data, gint data_len)
g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR);
- qd = (qq_data *) gc->proto_data;
-
switch (ret) {
case QQ_LOGIN_REPLY_OK:
purple_debug_info("QQ", "Login OK\n");
@@ -588,9 +585,14 @@ gboolean qq_process_keep_alive_2008(guint8 *data, gint data_len, PurpleConnectio
inet_ntoa(qd->my_ip), qd->my_port);
tm_local = localtime(&server_time);
- purple_debug_info("QQ", "Server time: %d-%d-%d, %d:%d:%d\n",
- (1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday,
- tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec);
+
+ if (tm_local != NULL)
+ purple_debug_info("QQ", "Server time: %d-%d-%d, %d:%d:%d\n",
+ (1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday,
+ tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec);
+ else
+ purple_debug_error("QQ", "Server time could not be parsed\n");
+
return TRUE;
}
@@ -811,11 +813,11 @@ static void captcha_request_destory(qq_captcha_request *captcha_req)
static void captcha_input_cancel_cb(qq_captcha_request *captcha_req,
PurpleRequestFields *fields)
{
- captcha_request_destory(captcha_req);
-
purple_connection_error_reason(captcha_req->gc,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
_("Failed captcha verification"));
+
+ captcha_request_destory(captcha_req);
}
static void captcha_input_ok_cb(qq_captcha_request *captcha_req,
diff --git a/libpurple/protocols/qq/qq_network.c b/libpurple/protocols/qq/qq_network.c
index 4c391d475b..09b4868766 100644
--- a/libpurple/protocols/qq/qq_network.c
+++ b/libpurple/protocols/qq/qq_network.c
@@ -482,13 +482,11 @@ static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond)
static void udp_pending(gpointer data, gint source, PurpleInputCondition cond)
{
PurpleConnection *gc = NULL;
- qq_data *qd;
guint8 *buf;
gint buf_len;
gc = (PurpleConnection *) data;
- g_return_if_fail(gc != NULL && gc->proto_data != NULL);
- qd = (qq_data *) gc->proto_data;
+ g_return_if_fail(gc != NULL);
if(cond != PURPLE_INPUT_READ) {
purple_connection_error_reason(gc,
@@ -748,14 +746,12 @@ static void connect_cb(gpointer data, gint source, const gchar *error_message)
{
PurpleConnection *gc;
qq_data *qd;
- PurpleAccount *account ;
qq_connection *conn;
gc = (PurpleConnection *) data;
g_return_if_fail(gc != NULL && gc->proto_data != NULL);
qd = (qq_data *) gc->proto_data;
- account = purple_connection_get_account(gc);
/* conn_data will be destoryed */
qd->conn_data = NULL;
diff --git a/libpurple/protocols/qq/qq_process.c b/libpurple/protocols/qq/qq_process.c
index 56f00c8116..74d3b395b3 100644
--- a/libpurple/protocols/qq/qq_process.c
+++ b/libpurple/protocols/qq/qq_process.c
@@ -58,15 +58,12 @@ enum {
/* default process, decrypt and dump */
static void process_unknow_cmd(PurpleConnection *gc,const gchar *title, guint8 *data, gint data_len, guint16 cmd, guint16 seq)
{
- qq_data *qd;
gchar *msg;
g_return_if_fail(data != NULL && data_len != 0);
qq_show_packet(title, data, data_len);
- qd = (qq_data *) gc->proto_data;
-
qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
data, data_len,
">>> [%d] %s -> [default] decrypt and dump",
@@ -80,12 +77,8 @@ static void process_unknow_cmd(PurpleConnection *gc,const gchar *title, guint8 *
/* parse the reply to send_im */
static void do_im_ack(guint8 *data, gint data_len, PurpleConnection *gc)
{
- qq_data *qd;
-
g_return_if_fail(data != NULL && data_len != 0);
- qd = gc->proto_data;
-
if (data[0] != 0) {
purple_debug_warning("QQ", "Failed sent IM\n");
purple_notify_error(gc, _("Error"), _("Unable to send message."), NULL);
@@ -380,14 +373,11 @@ static void process_private_msg(guint8 *data, gint data_len, guint16 seq, Purple
/* Send ACK if the sys message needs an ACK */
static void request_server_ack(PurpleConnection *gc, gchar *funct_str, gchar *from, guint16 seq)
{
- qq_data *qd;
guint8 *raw_data;
gint bytes;
guint8 bar;
g_return_if_fail(funct_str != NULL && from != NULL);
- qd = (qq_data *) gc->proto_data;
-
bar = 0x1e;
raw_data = g_newa(guint8, strlen(funct_str) + strlen(from) + 16);
@@ -568,11 +558,9 @@ static void process_room_cmd_notify(PurpleConnection *gc,
void qq_update_room(PurpleConnection *gc, guint8 room_cmd, guint32 room_id)
{
- qq_data *qd;
gint ret;
- g_return_if_fail (gc != NULL && gc->proto_data != NULL);
- qd = (qq_data *) gc->proto_data;
+ g_return_if_fail (gc != NULL);
switch (room_cmd) {
case 0:
@@ -599,12 +587,10 @@ void qq_update_room(PurpleConnection *gc, guint8 room_cmd, guint32 room_id)
void qq_update_all_rooms(PurpleConnection *gc, guint8 room_cmd, guint32 room_id)
{
- qq_data *qd;
gboolean is_new_turn = FALSE;
guint32 next_id;
- g_return_if_fail (gc != NULL && gc->proto_data != NULL);
- qd = (qq_data *) gc->proto_data;
+ g_return_if_fail(gc != NULL);
next_id = qq_room_get_next(gc, room_id);
purple_debug_info("QQ", "Update rooms, next id %u, prev id %u\n", next_id, room_id);
@@ -689,11 +675,9 @@ void qq_update_all(PurpleConnection *gc, guint16 cmd)
static void update_all_rooms_online(PurpleConnection *gc, guint8 room_cmd, guint32 room_id)
{
- qq_data *qd;
guint32 next_id;
- g_return_if_fail (gc != NULL && gc->proto_data != NULL);
- qd = (qq_data *) gc->proto_data;
+ g_return_if_fail (gc != NULL);
next_id = qq_room_get_next_conv(gc, room_id);
if (next_id <= 0 && room_id <= 0) {
diff --git a/libpurple/protocols/qq/qq_trans.c b/libpurple/protocols/qq/qq_trans.c
index 607fd5dbe1..19361955d1 100644
--- a/libpurple/protocols/qq/qq_trans.c
+++ b/libpurple/protocols/qq/qq_trans.c
@@ -109,11 +109,9 @@ guint32 qq_trans_get_ship(qq_transaction *trans)
static qq_transaction *trans_create(PurpleConnection *gc, gint fd,
guint16 cmd, guint16 seq, guint8 *data, gint data_len, guint32 update_class, guint32 ship32)
{
- qq_data *qd;
qq_transaction *trans;
- g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, NULL);
- qd = (qq_data *) gc->proto_data;
+ g_return_val_if_fail(gc != NULL, NULL);
trans = g_new0(qq_transaction, 1);
@@ -138,10 +136,11 @@ static qq_transaction *trans_create(PurpleConnection *gc, gint fd,
/* Remove a packet with seq from send trans */
static void trans_remove(PurpleConnection *gc, qq_transaction *trans)
{
- qq_data *qd = (qq_data *)gc->proto_data;
+ qq_data *qd;
- g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+ g_return_if_fail(gc != NULL);
qd = (qq_data *) gc->proto_data;
+ g_return_if_fail(qd != NULL);
g_return_if_fail(trans != NULL);
#if 0
diff --git a/libpurple/protocols/qq/send_file.c b/libpurple/protocols/qq/send_file.c
index 7bc427836b..2e48e09582 100644
--- a/libpurple/protocols/qq/send_file.c
+++ b/libpurple/protocols/qq/send_file.c
@@ -637,10 +637,8 @@ static void _qq_xfer_cancel(PurpleXfer *xfer)
{
PurpleConnection *gc;
PurpleAccount *account;
- guint16 *seq;
g_return_if_fail (xfer != NULL);
- seq = (guint16 *) xfer->data;
account = purple_xfer_get_account(xfer);
gc = purple_account_get_connection(account);
@@ -670,10 +668,8 @@ static void _qq_xfer_recv_init(PurpleXfer *xfer)
{
PurpleConnection *gc;
PurpleAccount *account;
- ft_info *info;
- g_return_if_fail (xfer != NULL && xfer->data != NULL);
- info = (ft_info *) xfer->data;
+ g_return_if_fail(xfer != NULL);
account = purple_xfer_get_account(xfer);
gc = purple_account_get_connection(account);
@@ -752,7 +748,7 @@ void qq_process_recv_file_accept(guint8 *data, gint data_len, guint32 sender_uid
g_return_if_fail (data != NULL && data_len != 0);
qd = (qq_data *) gc->proto_data;
xfer = qd->xfer;
- info = (ft_info *) qd->xfer->data;
+ info = (ft_info *) xfer->data;
if (data_len <= 30 + QQ_CONN_INFO_LEN) {
purple_debug_warning("QQ", "Received file reject message is empty\n");
@@ -761,7 +757,7 @@ void qq_process_recv_file_accept(guint8 *data, gint data_len, guint32 sender_uid
bytes = 18 + 12; /* skip 30 bytes */
qq_get_conn_info(info, data + bytes);
- _qq_xfer_init_socket(qd->xfer);
+ _qq_xfer_init_socket(xfer);
_qq_xfer_init_udp_channel(info);
_qq_send_packet_file_notifyip(gc, sender_uid);
diff --git a/libpurple/protocols/sametime/sametime.c b/libpurple/protocols/sametime/sametime.c
index be9382c938..0c1eb33a7d 100644
--- a/libpurple/protocols/sametime/sametime.c
+++ b/libpurple/protocols/sametime/sametime.c
@@ -2133,7 +2133,7 @@ static struct mwServiceConference *mw_srvc_conf_new(struct mwSession *s) {
static void ft_incoming_cancel(PurpleXfer *xfer) {
- /* incoming transfer rejected or canceled in-progress */
+ /* incoming transfer rejected or cancelled in-progress */
struct mwFileTransfer *ft = xfer->data;
if(ft) mwFileTransfer_reject(ft);
}
diff --git a/libpurple/protocols/silc/chat.c b/libpurple/protocols/silc/chat.c
index 6ecbe5c314..729d6cdc13 100644
--- a/libpurple/protocols/silc/chat.c
+++ b/libpurple/protocols/silc/chat.c
@@ -1395,7 +1395,7 @@ PurpleRoomlist *silcpurple_roomlist_get_list(PurpleConnection *gc)
if (sg->roomlist)
purple_roomlist_unref(sg->roomlist);
- sg->roomlist_canceled = FALSE;
+ 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);
@@ -1429,6 +1429,6 @@ void silcpurple_roomlist_cancel(PurpleRoomlist *list)
if (sg->roomlist == list) {
purple_roomlist_unref(sg->roomlist);
sg->roomlist = NULL;
- sg->roomlist_canceled = TRUE;
+ sg->roomlist_cancelled = TRUE;
}
}
diff --git a/libpurple/protocols/silc/ops.c b/libpurple/protocols/silc/ops.c
index df4a338667..fac1fde0e5 100644
--- a/libpurple/protocols/silc/ops.c
+++ b/libpurple/protocols/silc/ops.c
@@ -1455,7 +1455,7 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
int usercount;
PurpleRoomlistRoom *room;
- if (sg->roomlist_canceled)
+ if (sg->roomlist_cancelled)
break;
if (error != SILC_STATUS_OK) {
diff --git a/libpurple/protocols/silc/silc.c b/libpurple/protocols/silc/silc.c
index 2262106040..3b8686ef86 100644
--- a/libpurple/protocols/silc/silc.c
+++ b/libpurple/protocols/silc/silc.c
@@ -2117,7 +2117,9 @@ static PurplePluginProtocolInfo prpl_info =
NULL, /* get_account_text_table */
NULL, /* initiate_media */
NULL, /* get_media_caps */
- NULL /* get_moods */
+ NULL, /* get_moods */
+ NULL, /* set_public_alias */
+ NULL /* get_public_alias */
};
static PurplePluginInfo info =
diff --git a/libpurple/protocols/silc/silcpurple.h b/libpurple/protocols/silc/silcpurple.h
index 50a451ed19..937e0cc358 100644
--- a/libpurple/protocols/silc/silcpurple.h
+++ b/libpurple/protocols/silc/silcpurple.h
@@ -85,7 +85,7 @@ typedef struct SilcPurpleStruct {
SilcMimeAssembler mimeass;
unsigned int detaching : 1;
unsigned int resuming : 1;
- unsigned int roomlist_canceled : 1;
+ unsigned int roomlist_cancelled : 1;
unsigned int chpk : 1;
} *SilcPurple;
diff --git a/libpurple/protocols/silc10/chat.c b/libpurple/protocols/silc10/chat.c
index b9c903ef51..d87598f327 100644
--- a/libpurple/protocols/silc10/chat.c
+++ b/libpurple/protocols/silc10/chat.c
@@ -1417,7 +1417,7 @@ PurpleRoomlist *silcpurple_roomlist_get_list(PurpleConnection *gc)
if (sg->roomlist)
purple_roomlist_unref(sg->roomlist);
- sg->roomlist_canceled = FALSE;
+ 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);
@@ -1451,6 +1451,6 @@ void silcpurple_roomlist_cancel(PurpleRoomlist *list)
if (sg->roomlist == list) {
purple_roomlist_unref(sg->roomlist);
sg->roomlist = NULL;
- sg->roomlist_canceled = TRUE;
+ sg->roomlist_cancelled = TRUE;
}
}
diff --git a/libpurple/protocols/silc10/ops.c b/libpurple/protocols/silc10/ops.c
index f4b42fe3b7..8e3566e486 100644
--- a/libpurple/protocols/silc10/ops.c
+++ b/libpurple/protocols/silc10/ops.c
@@ -1444,7 +1444,7 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
int usercount;
PurpleRoomlistRoom *room;
- if (sg->roomlist_canceled)
+ if (sg->roomlist_cancelled)
break;
if (!success) {
diff --git a/libpurple/protocols/silc10/silc.c b/libpurple/protocols/silc10/silc.c
index faa78b5aa1..dd035e2053 100644
--- a/libpurple/protocols/silc10/silc.c
+++ b/libpurple/protocols/silc10/silc.c
@@ -1842,7 +1842,10 @@ static PurplePluginProtocolInfo prpl_info =
sizeof(PurplePluginProtocolInfo), /* struct_size */
NULL, /* get_account_text_table */
NULL, /* initiate_media */
- NULL /* can_do_media */
+ NULL, /* get_media_caps */
+ NULL, /* get_moods */
+ NULL, /* set_public_alias */
+ NULL /* get_public_alias */
};
static PurplePluginInfo info =
diff --git a/libpurple/protocols/silc10/silcpurple.h b/libpurple/protocols/silc10/silcpurple.h
index 86ba056614..a24d8a0b9d 100644
--- a/libpurple/protocols/silc10/silcpurple.h
+++ b/libpurple/protocols/silc10/silcpurple.h
@@ -80,7 +80,7 @@ typedef struct SilcPurpleStruct {
#endif
unsigned int detaching : 1;
unsigned int resuming : 1;
- unsigned int roomlist_canceled : 1;
+ unsigned int roomlist_cancelled : 1;
unsigned int chpk : 1;
} *SilcPurple;
diff --git a/libpurple/protocols/simple/simple.c b/libpurple/protocols/simple/simple.c
index 34e72854e4..1f1d3f81b2 100644
--- a/libpurple/protocols/simple/simple.c
+++ b/libpurple/protocols/simple/simple.c
@@ -1653,6 +1653,7 @@ static void process_input(struct simple_account_data *sip, struct sip_connection
}
purple_debug(PURPLE_DEBUG_MISC, "simple", "in process response response: %d\n", msg->response);
process_input_message(sip, msg);
+ sipmsg_free(msg);
} else {
purple_debug(PURPLE_DEBUG_MISC, "simple", "received a incomplete sip msg: %s\n", conn->inbuf);
}
@@ -1663,14 +1664,17 @@ static void simple_udp_process(gpointer data, gint source, PurpleInputCondition
struct simple_account_data *sip = gc->proto_data;
struct sipmsg *msg;
int len;
- time_t currtime;
+ time_t currtime = time(NULL);
static char buffer[65536];
if((len = recv(source, buffer, sizeof(buffer) - 1, 0)) > 0) {
buffer[len] = '\0';
purple_debug_info("simple", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime), buffer);
msg = sipmsg_parse_msg(buffer);
- if(msg) process_input_message(sip, msg);
+ if (msg) {
+ process_input_message(sip, msg);
+ sipmsg_free(msg);
+ }
}
}
@@ -1777,10 +1781,14 @@ static void simple_udp_host_resolved_listen_cb(int listenfd, gpointer data) {
return;
}
+ /*
+ * TODO: Is it correct to set sip->fd to the listenfd? For the TCP
+ * listener we set sip->listenfd, but maybe UDP is different?
+ * Maybe we use the same fd for outgoing data or something?
+ */
sip->fd = listenfd;
sip->listenport = purple_network_get_port_from_fd(sip->fd);
- sip->listenfd = sip->fd;
sip->listenpa = purple_input_add(sip->fd, PURPLE_INPUT_READ, simple_udp_process, sip->gc);
@@ -1965,54 +1973,69 @@ static void simple_close(PurpleConnection *gc)
{
struct simple_account_data *sip = gc->proto_data;
- if(sip) {
- /* unregister */
- if (sip->registerstatus == SIMPLE_REGISTER_COMPLETE)
- {
- g_hash_table_foreach(sip->buddies,
- (GHFunc)simple_unsubscribe,
- (gpointer)sip);
+ if (!sip)
+ return;
- if(purple_account_get_bool(sip->account,
- "dopublish", TRUE))
- send_closed_publish(sip);
+ /* unregister */
+ if (sip->registerstatus == SIMPLE_REGISTER_COMPLETE)
+ {
+ g_hash_table_foreach(sip->buddies,
+ (GHFunc)simple_unsubscribe,
+ (gpointer)sip);
- do_register_exp(sip, 0);
- }
- connection_free_all(sip);
-
- if (sip->query_data != NULL)
- purple_dnsquery_destroy(sip->query_data);
-
- if (sip->srv_query_data != NULL)
- purple_srv_cancel(sip->srv_query_data);
-
- if (sip->listen_data != NULL)
- purple_network_listen_cancel(sip->listen_data);
-
- g_free(sip->servername);
- g_free(sip->username);
- g_free(sip->password);
- g_free(sip->registrar.nonce);
- g_free(sip->registrar.opaque);
- g_free(sip->registrar.target);
- g_free(sip->registrar.realm);
- g_free(sip->registrar.digest_session_key);
- g_free(sip->proxy.nonce);
- g_free(sip->proxy.opaque);
- g_free(sip->proxy.target);
- g_free(sip->proxy.realm);
- g_free(sip->proxy.digest_session_key);
- g_free(sip->publish_etag);
- if(sip->txbuf)
- purple_circ_buffer_destroy(sip->txbuf);
- g_free(sip->realhostname);
- if(sip->listenpa) purple_input_remove(sip->listenpa);
- if(sip->tx_handler) purple_input_remove(sip->tx_handler);
- if(sip->resendtimeout) purple_timeout_remove(sip->resendtimeout);
- if(sip->registertimeout) purple_timeout_remove(sip->registertimeout);
- }
- g_free(gc->proto_data);
+ if (purple_account_get_bool(sip->account, "dopublish", TRUE))
+ send_closed_publish(sip);
+
+ do_register_exp(sip, 0);
+ }
+ connection_free_all(sip);
+
+ if (sip->listenpa)
+ purple_input_remove(sip->listenpa);
+ if (sip->tx_handler)
+ purple_input_remove(sip->tx_handler);
+ if (sip->resendtimeout)
+ purple_timeout_remove(sip->resendtimeout);
+ if (sip->registertimeout)
+ purple_timeout_remove(sip->registertimeout);
+ if (sip->query_data != NULL)
+ purple_dnsquery_destroy(sip->query_data);
+
+ if (sip->srv_query_data != NULL)
+ purple_srv_cancel(sip->srv_query_data);
+
+ if (sip->listen_data != NULL)
+ purple_network_listen_cancel(sip->listen_data);
+
+ if (sip->fd >= 0)
+ close(sip->fd);
+ if (sip->listenfd >= 0)
+ close(sip->listenfd);
+
+ g_free(sip->servername);
+ g_free(sip->username);
+ g_free(sip->password);
+ g_free(sip->registrar.nonce);
+ g_free(sip->registrar.opaque);
+ g_free(sip->registrar.target);
+ g_free(sip->registrar.realm);
+ g_free(sip->registrar.digest_session_key);
+ g_free(sip->proxy.nonce);
+ g_free(sip->proxy.opaque);
+ g_free(sip->proxy.target);
+ g_free(sip->proxy.realm);
+ g_free(sip->proxy.digest_session_key);
+ g_free(sip->status);
+ g_hash_table_destroy(sip->buddies);
+ g_free(sip->regcallid);
+ while (sip->transactions)
+ transactions_remove(sip, sip->transactions->data);
+ g_free(sip->publish_etag);
+ if (sip->txbuf)
+ purple_circ_buffer_destroy(sip->txbuf);
+ g_free(sip->realhostname);
+
+ g_free(sip);
gc->proto_data = NULL;
}
@@ -2087,7 +2110,9 @@ static PurplePluginProtocolInfo prpl_info =
NULL, /* get_account_text_table */
NULL, /* initiate_media */
NULL, /* get_media_caps */
- NULL /* get_moods */
+ NULL, /* get_moods */
+ NULL, /* set_public_alias */
+ NULL /* get_public_alias */
};
diff --git a/libpurple/protocols/simple/sipmsg.c b/libpurple/protocols/simple/sipmsg.c
index e898e60d00..51411de2f0 100644
--- a/libpurple/protocols/simple/sipmsg.c
+++ b/libpurple/protocols/simple/sipmsg.c
@@ -55,15 +55,15 @@ struct sipmsg *sipmsg_parse_msg(const gchar *msg) {
}
struct sipmsg *sipmsg_parse_header(const gchar *header) {
- struct sipmsg *msg = g_new0(struct sipmsg,1);
- gchar **parts, **lines = g_strsplit(header,"\r\n",0);
+ struct sipmsg *msg;
+ gchar **parts, **lines;
gchar *dummy, *dummy2, *tmp;
const gchar *tmp2;
int i = 1;
+ lines = g_strsplit(header,"\r\n",0);
if(!lines[0]) {
g_strfreev(lines);
- g_free(msg);
return NULL;
}
@@ -71,10 +71,10 @@ struct sipmsg *sipmsg_parse_header(const gchar *header) {
if(!parts[0] || !parts[1] || !parts[2]) {
g_strfreev(parts);
g_strfreev(lines);
- g_free(msg);
return NULL;
}
+ msg = g_new0(struct sipmsg,1);
if(strstr(parts[0],"SIP")) { /* numeric response */
msg->method = g_strdup(parts[2]);
msg->response = strtol(parts[1],NULL,10);
@@ -90,7 +90,7 @@ struct sipmsg *sipmsg_parse_header(const gchar *header) {
if(!parts[0] || !parts[1]) {
g_strfreev(parts);
g_strfreev(lines);
- g_free(msg);
+ sipmsg_free(msg);
return NULL;
}
dummy = parts[1];
@@ -106,6 +106,7 @@ struct sipmsg *sipmsg_parse_header(const gchar *header) {
dummy2 = tmp;
}
sipmsg_add_header(msg, parts[0], dummy2);
+ g_free(dummy2);
g_strfreev(parts);
}
g_strfreev(lines);
@@ -116,9 +117,10 @@ struct sipmsg *sipmsg_parse_header(const gchar *header) {
if(msg->response) {
tmp2 = sipmsg_find_header(msg, "CSeq");
+ g_free(msg->method);
if(!tmp2) {
/* SHOULD NOT HAPPEN */
- msg->method = 0;
+ msg->method = NULL;
} else {
parts = g_strsplit(tmp2, " ", 2);
msg->method = g_strdup(parts[1]);
@@ -168,7 +170,7 @@ char *sipmsg_to_string(const struct sipmsg *msg) {
return g_string_free(outstr, FALSE);
}
void sipmsg_add_header(struct sipmsg *msg, const gchar *name, const gchar *value) {
- struct siphdrelement *element = g_new0(struct siphdrelement,1);
+ struct siphdrelement *element = g_new(struct siphdrelement,1);
element->name = g_strdup(name);
element->value = g_strdup(value);
msg->headers = g_slist_append(msg->headers, element);
diff --git a/libpurple/protocols/yahoo/libyahoo.c b/libpurple/protocols/yahoo/libyahoo.c
index 7c7c3047c3..a3871e1a72 100644
--- a/libpurple/protocols/yahoo/libyahoo.c
+++ b/libpurple/protocols/yahoo/libyahoo.c
@@ -265,7 +265,9 @@ static PurplePluginProtocolInfo prpl_info =
yahoo_get_account_text_table, /* get_account_text_table */
NULL, /* initiate_media */
NULL, /* get_media_caps */
- NULL /* get_moods */
+ NULL, /* get_moods */
+ NULL, /* set_public_alias */
+ NULL /* get_public_alias */
};
static PurplePluginInfo info =
@@ -325,7 +327,7 @@ 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);
- option = purple_account_option_bool_new(_("Use account proxy for SSL connections"), "proxy_ssl", FALSE);
+ 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
diff --git a/libpurple/protocols/yahoo/libyahoojp.c b/libpurple/protocols/yahoo/libyahoojp.c
index b7890a3c95..a7342f6bf9 100644
--- a/libpurple/protocols/yahoo/libyahoojp.c
+++ b/libpurple/protocols/yahoo/libyahoojp.c
@@ -161,7 +161,9 @@ static PurplePluginProtocolInfo prpl_info =
yahoojp_get_account_text_table, /* get_account_text_table */
NULL, /* initiate_media */
NULL, /* get_media_caps */
- NULL /* get_moods */
+ NULL, /* get_moods */
+ NULL, /* set_public_alias */
+ NULL /* get_public_alias */
};
static PurplePluginInfo info =
@@ -221,7 +223,7 @@ 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);
- option = purple_account_option_bool_new(_("Use account proxy for SSL connections"), "proxy_ssl", FALSE);
+ 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
diff --git a/libpurple/protocols/yahoo/libymsg.c b/libpurple/protocols/yahoo/libymsg.c
index aeb51e978c..a448b5a0cb 100644
--- a/libpurple/protocols/yahoo/libymsg.c
+++ b/libpurple/protocols/yahoo/libymsg.c
@@ -401,15 +401,13 @@ static void yahoo_do_group_check(PurpleAccount *account, GHashTable *ht, const c
PurpleBuddy *b;
PurpleGroup *g;
GSList *list, *i;
- gboolean onlist = 0;
+ gboolean onlist = FALSE;
char *oname = NULL;
- char **oname_p = &oname;
- GSList **list_p = &list;
- if (!g_hash_table_lookup_extended(ht, purple_normalize(account, name), (gpointer *) oname_p, (gpointer *) list_p))
- list = purple_find_buddies(account, name);
+ if (g_hash_table_lookup_extended(ht, name, (gpointer *)&oname, (gpointer *)&list))
+ g_hash_table_steal(ht, oname);
else
- g_hash_table_steal(ht, name);
+ list = purple_find_buddies(account, name);
for (i = list; i; i = i->next) {
b = i->data;
@@ -418,7 +416,7 @@ static void yahoo_do_group_check(PurpleAccount *account, GHashTable *ht, const c
purple_debug_misc("yahoo",
"Oh good, %s is in the right group (%s).\n", name, group);
list = g_slist_delete_link(list, i);
- onlist = 1;
+ onlist = TRUE;
break;
}
}
@@ -436,9 +434,9 @@ static void yahoo_do_group_check(PurpleAccount *account, GHashTable *ht, const c
if (list) {
if (!oname)
- oname = g_strdup(purple_normalize(account, name));
+ oname = g_strdup(name);
g_hash_table_insert(ht, oname, list);
- } else if (oname)
+ } else
g_free(oname);
}
@@ -504,8 +502,6 @@ static void yahoo_process_list_15(PurpleConnection *gc, struct yahoo_packet *pkt
char *temp = NULL;
YahooFriend *f = NULL; /* It's your friends. They're going to want you to share your StarBursts. */
/* But what if you had no friends? */
- PurpleBuddy *b;
- PurpleGroup *g;
YahooFederation fed = YAHOO_FEDERATION_NONE;
int stealth = 0;
@@ -551,7 +547,9 @@ 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 (!(b = purple_find_buddy(account, norm_bud))) {
+ if (!purple_find_buddy(account, norm_bud)) {
+ PurpleBuddy *b;
+ PurpleGroup *g;
if (!(g = purple_find_group(yd->current_list15_grp))) {
g = purple_group_new(yd->current_list15_grp);
purple_blist_add_group(g, NULL);
@@ -638,8 +636,6 @@ static void yahoo_process_list(PurpleConnection *gc, struct yahoo_packet *pkt)
GSList *l = pkt->hash;
gboolean export = FALSE;
gboolean got_serv_list = FALSE;
- PurpleBuddy *b;
- PurpleGroup *g;
YahooFriend *f = NULL;
PurpleAccount *account = purple_connection_get_account(gc);
YahooData *yd = gc->proto_data;
@@ -707,7 +703,9 @@ 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 (!(b = purple_find_buddy(account, norm_bud))) {
+ if (!purple_find_buddy(account, norm_bud)) {
+ PurpleBuddy *b;
+ PurpleGroup *g;
if (!(g = purple_find_group(grp))) {
g = purple_group_new(grp);
purple_blist_add_group(g, NULL);
@@ -843,12 +841,12 @@ static void yahoo_process_notify(PurpleConnection *gc, struct yahoo_packet *pkt,
default:
break;
}
-
+
if (*stat == '1')
serv_got_typing(gc, fed_from, 0, PURPLE_TYPING);
else
serv_got_typing_stopped(gc, fed_from);
-
+
if (fed_from != from)
g_free(fed_from);
@@ -1773,7 +1771,7 @@ static gchar *yahoo_auth16_get_cookie_b(gchar *headers)
tmp = &splits[i][14];
sem = strchr(tmp, ';');
- if(tmp != NULL) {
+ if (sem != NULL) {
tmp2 = g_strndup(tmp, sem - tmp);
purple_debug_info("yahoo", "Got needed part of B cookie: %s\n",
tmp2 ? tmp2 : "(null)");
@@ -1783,26 +1781,21 @@ static gchar *yahoo_auth16_get_cookie_b(gchar *headers)
}
}
+ g_strfreev(splits);
return tmp2;
}
-static void yahoo_auth16_stage2(PurpleUtilFetchUrlData *unused, gpointer user_data, const gchar *ret_data, size_t len, const gchar *error_message)
+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;
PurpleConnection *gc = auth_data->gc;
- YahooData *yd;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
gboolean try_login_on_error = FALSE;
purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage2\n");
- if (!PURPLE_CONNECTION_IS_VALID(gc)) {
- g_free(auth_data->seed);
- g_free(auth_data);
- g_return_if_reached();
- }
-
- yd = (YahooData *)gc->proto_data;
-
+ yd->url_datas = g_slist_remove(yd->url_datas, url_data);
+
if (error_message != NULL) {
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);
@@ -1909,18 +1902,15 @@ static void yahoo_auth16_stage2(PurpleUtilFetchUrlData *unused, gpointer user_da
g_free(auth_data);
}
-static void yahoo_auth16_stage1_cb(PurpleUtilFetchUrlData *unused, gpointer user_data, const gchar *ret_data, size_t len, const gchar *error_message)
+static void yahoo_auth16_stage1_cb(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;
PurpleConnection *gc = auth_data->gc;
+ YahooData *yd = purple_connection_get_protocol_data(gc);
purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage1_cb\n");
- if (!PURPLE_CONNECTION_IS_VALID(gc)) {
- g_free(auth_data->seed);
- g_free(auth_data);
- g_return_if_reached();
- }
+ yd->url_datas = g_slist_remove(yd->url_datas, url_data);
if (error_message != NULL) {
purple_debug_error("yahoo", "Login Failed, unable to retrieve login url: %s\n", error_message);
@@ -2022,6 +2012,8 @@ static void yahoo_auth16_stage1_cb(PurpleUtilFetchUrlData *unused, gpointer user
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);
g_free(token);
}
@@ -2030,6 +2022,7 @@ static void yahoo_auth16_stage1_cb(PurpleUtilFetchUrlData *unused, gpointer user
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;
struct yahoo_auth_data *auth_data = NULL;
@@ -2061,6 +2054,8 @@ static void yahoo_auth16_stage1(PurpleConnection *gc, const char *seed)
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);
g_free(url);
}
@@ -2707,6 +2702,7 @@ void yahoo_send_p2p_pkt(PurpleConnection *gc, const char *who, int val_13)
PurpleAccount *account;
YahooData *yd = gc->proto_data;
struct yahoo_p2p_data *p2p_data;
+ const char *norm_username;
f = yahoo_friend_find(gc, who);
account = purple_connection_get_account(gc);
@@ -2739,10 +2735,11 @@ void yahoo_send_p2p_pkt(PurpleConnection *gc, const char *who, int val_13)
sprintf(temp_str, "%d", ip);
base64_ip = purple_base64_encode( (guchar *)temp_str, strlen(temp_str) );
+ norm_username = purple_normalize(account, purple_account_get_username(account));
pkt = yahoo_packet_new(YAHOO_SERVICE_PEERTOPEER, YAHOO_STATUS_AVAILABLE, 0);
yahoo_packet_hash(pkt, "sssissis",
- 1, purple_normalize(account, purple_account_get_username(account)),
- 4, purple_normalize(account, purple_account_get_username(account)),
+ 1, norm_username,
+ 4, norm_username,
12, base64_ip, /* base64 encode ip */
61, 0, /* To-do : figure out what is 61 for?? */
2, "",
@@ -2762,6 +2759,7 @@ void yahoo_send_p2p_pkt(PurpleConnection *gc, const char *who, int val_13)
p2p_data->connection_type = YAHOO_P2P_WE_ARE_SERVER;
p2p_data->source = -1;
+ /* FIXME: Shouldn't this deal with the PurpleNetworkListenData* */
purple_network_listen(YAHOO_PAGER_PORT_P2P, SOCK_STREAM, yahoo_p2p_server_listen_cb, p2p_data);
g_free(base64_ip);
@@ -3596,14 +3594,26 @@ static void yahoo_got_pager_server(PurpleUtilFetchUrlData *url_data,
PurpleConnection *gc = yd->gc;
PurpleAccount *a = purple_connection_get_account(gc);
gchar **strings = NULL, *cs_server = NULL;
- int port = 0, stringslen = 0;
+ int port = purple_account_get_int(a, "port", YAHOO_PAGER_PORT);
+ int stringslen = 0;
+
+ 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)");
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Unable to connect: The server returned an empty 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,
+ _("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,
+ _("Unable to connect"));
+ }
+ }
} else {
strings = g_strsplit(url_text, "\r\n", -1);
@@ -3621,17 +3631,23 @@ static void yahoo_got_pager_server(PurpleUtilFetchUrlData *url_data,
}
if(cs_server) { /* got an address; get on with connecting */
- port = purple_account_get_int(a, "port", YAHOO_PAGER_PORT);
-
if(purple_proxy_connect(gc, a, cs_server, port, yahoo_got_connected, gc) == NULL)
purple_connection_error_reason(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)");
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Unable to connect: The server's response did not contain "
- "the necessary information"));
+
+ if(yahoo_is_japan(a)) { /* We don't know fallback hosts for Yahoo Japan :( */
+ purple_connection_error_reason(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,
+ _("Unable to connect"));
+ }
}
}
@@ -3644,6 +3660,8 @@ void yahoo_login(PurpleAccount *account) {
YahooData *yd = gc->proto_data = 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;
@@ -3676,12 +3694,14 @@ 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. */
- purple_util_fetch_url_request_len_with_account(
- purple_connection_get_account(gc),
+ 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, TRUE, NULL, FALSE, -1,
+ 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);
return;
}
@@ -3742,6 +3762,7 @@ void yahoo_close(PurpleConnection *gc) {
g_free(yd->cookie_y);
g_free(yd->cookie_t);
+ g_free(yd->cookie_b);
if (yd->txhandler)
purple_input_remove(yd->txhandler);
@@ -3785,13 +3806,12 @@ const char *yahoo_list_emblem(PurpleBuddy *b)
{
PurpleAccount *account;
PurpleConnection *gc;
- YahooData *yd;
YahooFriend *f;
PurplePresence *presence;
if (!b || !(account = purple_buddy_get_account(b)) ||
!(gc = purple_account_get_connection(account)) ||
- !(yd = gc->proto_data))
+ !gc->proto_data)
return NULL;
f = yahoo_friend_find(gc, purple_buddy_get_name(b));
@@ -3886,7 +3906,6 @@ static void yahoo_game(PurpleBlistNode *node, gpointer data) {
PurpleBuddy *buddy;
PurpleConnection *gc;
- YahooData *yd;
const char *game;
char *game2;
char *t;
@@ -3897,7 +3916,6 @@ static void yahoo_game(PurpleBlistNode *node, gpointer data) {
buddy = (PurpleBuddy *) node;
gc = purple_account_get_connection(purple_buddy_get_account(buddy));
- yd = (YahooData *) gc->proto_data;
f = yahoo_friend_find(gc, purple_buddy_get_name(buddy));
if (!f)
@@ -4370,6 +4388,8 @@ static void yahoo_get_sms_carrier_cb(PurpleUtilFetchUrlData *url_data, gpointer
PurpleAccount *account = purple_connection_get_account(gc);
PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, 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));
@@ -4463,7 +4483,9 @@ static void yahoo_get_sms_carrier(PurpleConnection *gc, gpointer data)
g_free(request);
g_free(validate_request_str);
- if (!url_data) {
+ 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));
@@ -4917,7 +4939,6 @@ void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g)
struct yahoo_packet *pkt;
const char *group = NULL;
char *group2;
- YahooFriend *f;
const char *bname;
const char *fed_bname;
YahooFederation fed = YAHOO_FEDERATION_NONE;
@@ -4929,7 +4950,6 @@ void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g)
if (!purple_privacy_check(purple_connection_get_account(gc), bname))
return;
- f = yahoo_friend_find(gc, bname);
fed = yahoo_get_federation_from_name(bname);
if (fed != YAHOO_FEDERATION_NONE)
fed_bname += 4;
@@ -5192,15 +5212,11 @@ yahoopurple_cmd_chat_join(PurpleConversation *conv, const char *cmd,
{
GHashTable *comp;
PurpleConnection *gc;
- YahooData *yd;
- int id;
if (!args || !args[0])
return PURPLE_CMD_RET_FAILED;
gc = purple_conversation_get_gc(conv);
- yd = gc->proto_data;
- id = yd->conf_id;
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);
diff --git a/libpurple/protocols/yahoo/libymsg.h b/libpurple/protocols/yahoo/libymsg.h
index 7499c68add..a6cf03cece 100644
--- a/libpurple/protocols/yahoo/libymsg.h
+++ b/libpurple/protocols/yahoo/libymsg.h
@@ -30,6 +30,7 @@
#include "prpl.h"
#define YAHOO_PAGER_HOST_REQ_URL "http://vcs1.msg.yahoo.com/capacity"
+#define YAHOO_PAGER_HOST_FALLBACK "scsa.msg.yahoo.com"
#define YAHOO_PAGER_PORT 5050
#define YAHOO_PAGER_PORT_P2P 5101
#define YAHOO_LOGIN_URL "https://login.yahoo.com/config/pwtoken_login?src=ymsgr&ts=&token=%s"
diff --git a/libpurple/protocols/yahoo/util.c b/libpurple/protocols/yahoo/util.c
index c119b2d095..57bbb84b6b 100644
--- a/libpurple/protocols/yahoo/util.c
+++ b/libpurple/protocols/yahoo/util.c
@@ -33,10 +33,21 @@
#include <string.h>
gboolean
-yahoo_account_use_http_proxy(PurpleConnection *conn)
+yahoo_account_use_http_proxy(PurpleConnection *pc)
{
- PurpleProxyInfo *ppi = purple_proxy_get_setup(conn->account);
- return (ppi->type == PURPLE_PROXY_HTTP || ppi->type == PURPLE_PROXY_USE_ENVVAR);
+ PurpleAccount *account = purple_connection_get_account(pc);
+ PurpleProxyInfo *ppi = NULL;
+ PurpleProxyType type = PURPLE_PROXY_NONE;
+ gboolean proxy_ssl = purple_account_get_bool(account, "proxy_ssl", FALSE);
+
+ if(proxy_ssl)
+ ppi = purple_proxy_get_setup(account);
+ else
+ ppi = purple_global_proxy_get_info();
+
+ type = purple_proxy_info_get_type(ppi);
+
+ return (type == PURPLE_PROXY_HTTP || type == PURPLE_PROXY_USE_ENVVAR);
}
/*
diff --git a/libpurple/protocols/yahoo/yahoo_aliases.c b/libpurple/protocols/yahoo/yahoo_aliases.c
index fe5286fc87..3b0fc1f3d7 100644
--- a/libpurple/protocols/yahoo/yahoo_aliases.c
+++ b/libpurple/protocols/yahoo/yahoo_aliases.c
@@ -145,7 +145,7 @@ yahoo_fetch_aliases_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, con
if (alias != NULL) {
serv_got_alias(gc, yid, alias);
purple_debug_info("yahoo", "Fetched alias '%s' (%s)\n", alias, id);
- } else if (buddy_alias != NULL && strcmp(buddy_alias, "") != 0) {
+ } 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 */
yahoo_update_alias(gc, yid, buddy_alias);
purple_debug_info("yahoo", "Sent updated alias '%s'\n", buddy_alias);
diff --git a/libpurple/protocols/yahoo/yahoo_doodle.c b/libpurple/protocols/yahoo/yahoo_doodle.c
index 277adb74fa..f3f44a2631 100644
--- a/libpurple/protocols/yahoo/yahoo_doodle.c
+++ b/libpurple/protocols/yahoo/yahoo_doodle.c
@@ -372,7 +372,7 @@ 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_CANCELED;
+ wb->state = DOODLE_STATE_CANCELLED;
purple_whiteboard_destroy(wb);
}
@@ -460,7 +460,7 @@ void yahoo_doodle_end(PurpleWhiteboard *wb)
/* g_debug_debug("yahoo", "doodle: yahoo_doodle_end()\n"); */
- if (gc && wb->state != DOODLE_STATE_CANCELED)
+ if (gc && wb->state != DOODLE_STATE_CANCELLED)
yahoo_doodle_command_send_shutdown(gc, wb->who);
g_free(ds->imv_key);
diff --git a/libpurple/protocols/yahoo/yahoo_doodle.h b/libpurple/protocols/yahoo/yahoo_doodle.h
index 3fda73b771..91329d4595 100644
--- a/libpurple/protocols/yahoo/yahoo_doodle.h
+++ b/libpurple/protocols/yahoo/yahoo_doodle.h
@@ -56,7 +56,7 @@
#define DOODLE_STATE_REQUESTING 0
#define DOODLE_STATE_REQUESTED 1
#define DOODLE_STATE_ESTABLISHED 2
-#define DOODLE_STATE_CANCELED 3
+#define DOODLE_STATE_CANCELLED 3
/* Doodle canvas dimensions */
#define DOODLE_CANVAS_WIDTH 368
diff --git a/libpurple/protocols/yahoo/yahoo_filexfer.c b/libpurple/protocols/yahoo/yahoo_filexfer.c
index 690f5796c9..b1f1d2aea6 100644
--- a/libpurple/protocols/yahoo/yahoo_filexfer.c
+++ b/libpurple/protocols/yahoo/yahoo_filexfer.c
@@ -983,8 +983,8 @@ static void yahoo_xfer_dns_connected_15(GSList *hosts, gpointer data, const char
struct yahoo_xfer_data *xd;
struct sockaddr_in *addr;
struct yahoo_packet *pkt;
- long actaddr;
- long a,b,c,d;
+ unsigned long actaddr;
+ unsigned char a,b,c,d;
PurpleConnection *gc;
PurpleAccount *account;
YahooData *yd;
@@ -1018,19 +1018,19 @@ static void yahoo_xfer_dns_connected_15(GSList *hosts, gpointer data, const char
/* TODO:actually, u must try with addr no.1 , if its not working addr no.2 ..... */
addr = hosts->data;
actaddr = addr->sin_addr.s_addr;
- d = actaddr % 256;
- actaddr = (actaddr - d) / 256;
- c = actaddr % 256;
- actaddr = (actaddr - c) / 256;
- b = actaddr % 256;
- actaddr = (actaddr - b) / 256;
- a = actaddr;
+ d = actaddr & 0xff;
+ actaddr >>= 8;
+ c = actaddr & 0xff;
+ actaddr >>= 8;
+ 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("%ld.%ld.%ld.%ld", d, c, b, a);
+ url = g_strdup_printf("%u.%u.%u.%u", d, c, b, a);
/* Free the address... */
g_free(hosts->data);
@@ -1235,14 +1235,14 @@ static void yahoo_xfer_connected_15(gpointer data, gint source, const gchar *err
PurpleXfer *xfer;
struct yahoo_xfer_data *xd;
PurpleAccount *account;
- YahooData* yd;
+ PurpleConnection *gc;
if (!(xfer = data))
return;
if (!(xd = xfer->data))
return;
- yd = xd->gc->proto_data;
- account = purple_connection_get_account(xd->gc);
+ 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."));
@@ -1253,7 +1253,7 @@ static void yahoo_xfer_connected_15(gpointer data, gint source, const gchar *err
if (xd->txbuflen == 0)
{
gchar* cookies;
- cookies = yahoo_get_cookies(xd->gc);
+ cookies = yahoo_get_cookies(gc);
if(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND && xd->status_15 == ACCEPTED)
{
if(xd->info_val_249 == 2)
diff --git a/libpurple/protocols/yahoo/yahoochat.c b/libpurple/protocols/yahoo/yahoochat.c
index 7453990ea5..0f8a042598 100644
--- a/libpurple/protocols/yahoo/yahoochat.c
+++ b/libpurple/protocols/yahoo/yahoochat.c
@@ -121,7 +121,6 @@ void yahoo_process_conference_invite(PurpleConnection *gc, struct yahoo_packet *
char *msg = NULL;
GString *members = NULL;
GHashTable *components;
- PurpleConversation *c = NULL;
if ( (pkt->status == 2) || (pkt->status == 11) )
return; /* Status is 11 when we are being notified about invitation being sent to someone else */
@@ -133,7 +132,7 @@ void yahoo_process_conference_invite(PurpleConnection *gc, struct yahoo_packet *
if (pair->key == 57)
{
room = yahoo_string_decode(gc, pair->value, FALSE);
- if((c = yahoo_find_conference(gc, room)))
+ if (yahoo_find_conference(gc, room) != NULL)
{
/* Looks like we got invited to an already open conference. */
/* Laters: Should we accept this conference rather than ignoring the invitation ? */
@@ -618,9 +617,6 @@ void yahoo_process_chat_exit(PurpleConnection *gc, struct yahoo_packet *pkt)
char *who = NULL;
char *room = NULL;
GSList *l;
- YahooData *yd;
-
- yd = gc->proto_data;
for (l = pkt->hash; l; l = l->next) {
struct yahoo_pair *pair = l->data;
@@ -639,8 +635,7 @@ void yahoo_process_chat_exit(PurpleConnection *gc, struct yahoo_packet *pkt)
purple_conv_chat_remove_user(PURPLE_CONV_CHAT(c), who, NULL);
}
- if (room)
- g_free(room);
+ g_free(room);
}
void yahoo_process_chat_message(PurpleConnection *gc, struct yahoo_packet *pkt)
@@ -880,7 +875,6 @@ static void yahoo_chat_leave(PurpleConnection *gc, const char *room, const char
{
YahooData *yd = gc->proto_data;
struct yahoo_packet *pkt;
- PurpleConversation *c;
char *eroom;
gboolean utf8 = 1;
@@ -905,7 +899,7 @@ static void yahoo_chat_leave(PurpleConnection *gc, const char *room, const char
yd->chat_name = NULL;
}
- if ((c = purple_find_chat(gc, YAHOO_CHAT_ID)))
+ if (purple_find_chat(gc, YAHOO_CHAT_ID) != NULL)
serv_got_chat_left(gc, YAHOO_CHAT_ID);
if (!logout)
diff --git a/libpurple/protocols/zephyr/ZVariables.c b/libpurple/protocols/zephyr/ZVariables.c
index f11ba9d91c..028a8bdda8 100644
--- a/libpurple/protocols/zephyr/ZVariables.c
+++ b/libpurple/protocols/zephyr/ZVariables.c
@@ -30,10 +30,10 @@ char *ZGetVariable(var)
if ((varfile = get_localvarfile()) == NULL)
return ((char *)0);
- if ((ret = get_varval(varfile, var)) != ZERR_NONE) {
- g_free(varfile);
+ ret = get_varval(varfile, var);
+ g_free(varfile);
+ if (ret != ZERR_NONE)
return ret;
- }
#ifdef WIN32
varfile = g_strdup("C:\\zephyr\\zephyr.var");
diff --git a/libpurple/protocols/zephyr/zephyr.c b/libpurple/protocols/zephyr/zephyr.c
index 2168191b1b..4a86b6ac7c 100644
--- a/libpurple/protocols/zephyr/zephyr.c
+++ b/libpurple/protocols/zephyr/zephyr.c
@@ -2909,7 +2909,9 @@ static PurplePluginProtocolInfo prpl_info = {
NULL, /* get_account_text_table */
NULL, /* initate_media */
NULL, /* get_media_caps */
- NULL /* get_moods */
+ NULL, /* get_moods */
+ NULL, /* set_public_alias */
+ NULL /* get_public_alias */
};
static PurplePluginInfo info = {
diff --git a/libpurple/proxy.c b/libpurple/proxy.c
index 282b3572f0..63b0cc8688 100644
--- a/libpurple/proxy.c
+++ b/libpurple/proxy.c
@@ -379,11 +379,16 @@ _proxy_fill_hostinfo(PurpleProxyInfo *info, char *host, int default_port)
char *d;
d = g_strrstr(host, ":");
- if (d)
+ if (d) {
*d = '\0';
- d++;
- if (*d)
- sscanf(d, "%d", &port);
+
+ d++;
+ if (*d)
+ sscanf(d, "%d", &port);
+
+ if (port == 0)
+ port = default_port;
+ }
purple_proxy_info_set_host(info, host);
purple_proxy_info_set_port(info, port);
@@ -1018,7 +1023,7 @@ http_canread(gpointer data, gint source, PurpleInputCondition cond)
g_free(response);
- } else if((header = g_strrstr((const char *)connect_data->read_buffer, "Proxy-Authenticate: Basic"))) {
+ } else if (g_strrstr((const char *)connect_data->read_buffer, "Proxy-Authenticate: Basic") != NULL) {
gchar *t1, *t2;
const char *username, *password;
diff --git a/libpurple/prpl.h b/libpurple/prpl.h
index 4ed0def2fc..26493484e6 100644
--- a/libpurple/prpl.h
+++ b/libpurple/prpl.h
@@ -52,6 +52,13 @@ typedef enum {
typedef struct _PurpleBuddyIconSpec PurpleBuddyIconSpec;
/**
+ * A description of a file transfer thumbnail specification.
+ * This tells the UI if and what image formats the prpl support for file
+ * transfer thumbnails.
+ */
+typedef struct _PurpleThumbnailSpec PurpleThumbnailSpec;
+
+/**
* 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.
*/
@@ -573,8 +580,48 @@ struct _PurplePluginProtocolInfo
/**
* Returns an array of "PurpleMood"s, with the last one having
* "mood" set to @c NULL.
+ * @since 2.7.0
*/
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
+ * *asynchronously* (if it knows immediately that the set will fail,
+ * 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,
+ * 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
+ * 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
+ */
+ void (*get_public_alias)(PurpleConnection *gc,
+ PurpleGetPublicAliasSuccessCallback success_cb,
+ PurpleGetPublicAliasFailureCallback failure_cb);
};
#define PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl, member) \
diff --git a/libpurple/purple-url-handler b/libpurple/purple-url-handler
index 083070b1ae..4407d8fb07 100755
--- a/libpurple/purple-url-handler
+++ b/libpurple/purple-url-handler
@@ -73,7 +73,8 @@ def bring_account_online(account):
def findaccount(protocolname, accountname="", matcher=None):
if matcher:
for account in cpurple.PurpleAccountsGetAll():
- if accountname != "" and accountname != cpurple.PurpleAccountGetUsername(a):
+ if (protocolname != cpurple.PurpleAccountGetProtocolID(account)) or \
+ (accountname != "" and accountname != cpurple.PurpleAccountGetUsername(account)):
continue
if matcher(account):
bring_account_online(account)
@@ -182,7 +183,7 @@ def icq(uri):
def irc(uri):
protocol = "prpl-irc"
- match = re.match(r"^irc:(//([^/]*)/)?([^?]*)(\?(.*))?", uri)
+ match = re.match(r"^irc:(//([^/]*))?/?([^?]*)(\?(.*))?", uri)
if not match:
print "Invalid irc URI: %s" % uri
return
@@ -207,7 +208,7 @@ def irc(uri):
def correct_server(account):
username = cpurple.PurpleAccountGetUsername(account)
- return ("@" in username) and (server == (username.split("@"))[1])
+ return ((server == "") or ("@" in username) and (server == (username.split("@"))[1]))
account = findaccount(protocol, matcher=correct_server)
diff --git a/libpurple/request.c b/libpurple/request.c
index fe7198c799..391e993d8d 100644
--- a/libpurple/request.c
+++ b/libpurple/request.c
@@ -1317,6 +1317,29 @@ purple_request_action(void *handle, const char *title, const char *primary,
}
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 *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);
+ 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,
@@ -1348,6 +1371,46 @@ purple_request_action_varg(void *handle, const char *title,
}
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)
+{
+ PurpleRequestUiOps *ops;
+
+ g_return_val_if_fail(action_count > 0, NULL);
+
+ ops = purple_request_get_ui_ops();
+
+ if (ops != NULL && ops->request_action_with_icon != NULL) {
+ PurpleRequestInfo *info;
+
+ info = g_new0(PurpleRequestInfo, 1);
+ info->type = PURPLE_REQUEST_ACTION;
+ 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);
+
+ handles = g_list_append(handles, info);
+
+ 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;
+}
+
+
+void *
purple_request_fields(void *handle, const char *title, const char *primary,
const char *secondary, PurpleRequestFields *fields,
const char *ok_text, GCallback ok_cb,
diff --git a/libpurple/request.h b/libpurple/request.h
index 049b043b93..2083f7936b 100644
--- a/libpurple/request.h
+++ b/libpurple/request.h
@@ -237,10 +237,18 @@ typedef struct
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);
+
void (*_purple_reserved1)(void);
void (*_purple_reserved2)(void);
void (*_purple_reserved3)(void);
- void (*_purple_reserved4)(void);
} PurpleRequestUiOps;
typedef void (*PurpleRequestInputCb)(void *, const char *);
@@ -1393,6 +1401,29 @@ void *purple_request_action_varg(void *handle, const char *title,
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
+ */
+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, ...);
+
+/**
+ * <tt>va_list</tt> version of purple_request_action_with_icon();
+ * see its documentation.
+ * @since 2.7.0
+ */
+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);
+
+
+/**
* Displays groups of fields for the user to fill in.
*
* @param handle The plugin or connection handle. For some things this
@@ -1477,6 +1508,19 @@ void purple_request_close_with_handle(void *handle);
_("_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.
*
diff --git a/libpurple/roomlist.h b/libpurple/roomlist.h
index 8158b689c2..de9270f002 100644
--- a/libpurple/roomlist.h
+++ b/libpurple/roomlist.h
@@ -57,7 +57,7 @@ typedef enum
} PurpleRoomlistFieldType;
#include "account.h"
-#include "glib.h"
+#include <glib.h>
/**************************************************************************/
/** Data Structures */
diff --git a/libpurple/stun.c b/libpurple/stun.c
index c6f71da0b4..a6f516155d 100644
--- a/libpurple/stun.c
+++ b/libpurple/stun.c
@@ -105,11 +105,11 @@ static void close_stun_conn(struct stun_conn *sc) {
}
static void do_callbacks(void) {
- while(callbacks) {
+ while (callbacks) {
StunCallback cb = callbacks->data;
- if(cb)
+ if (cb)
cb(&nattype);
- callbacks = g_slist_remove(callbacks, cb);
+ callbacks = g_slist_delete_link(callbacks, callbacks);
}
}
@@ -280,7 +280,6 @@ static void hbn_listen_cb(int fd, gpointer data) {
GSList *hosts = data;
struct stun_conn *sc;
static struct stun_header hdr_data;
- int ret;
if(fd < 0) {
nattype.status = PURPLE_STUN_STATUS_UNKNOWN;
@@ -298,15 +297,14 @@ static void hbn_listen_cb(int fd, gpointer data) {
sc->incb = purple_input_add(fd, PURPLE_INPUT_READ, reply_cb, sc);
- ret = GPOINTER_TO_INT(hosts->data);
- hosts = g_slist_remove(hosts, hosts->data);
+ hosts = g_slist_delete_link(hosts, hosts);
memcpy(&(sc->addr), hosts->data, sizeof(struct sockaddr_in));
g_free(hosts->data);
- hosts = g_slist_remove(hosts, hosts->data);
- while(hosts) {
- hosts = g_slist_remove(hosts, hosts->data);
+ hosts = g_slist_delete_link(hosts, hosts);
+ while (hosts) {
+ hosts = g_slist_delete_link(hosts, hosts);
g_free(hosts->data);
- hosts = g_slist_remove(hosts, hosts->data);
+ hosts = g_slist_delete_link(hosts, hosts);
}
hdr_data.type = htons(MSGTYPE_BINDINGREQUEST);
@@ -341,10 +339,10 @@ static void hbn_cb(GSList *hosts, gpointer data, const char *error_message) {
}
if (!purple_network_listen_range(12108, 12208, SOCK_DGRAM, hbn_listen_cb, hosts)) {
- while(hosts) {
- hosts = g_slist_remove(hosts, hosts->data);
+ while (hosts) {
+ hosts = g_slist_delete_link(hosts, hosts);
g_free(hosts->data);
- hosts = g_slist_remove(hosts, hosts->data);
+ hosts = g_slist_delete_link(hosts, hosts);
}
nattype.status = PURPLE_STUN_STATUS_UNKNOWN;
diff --git a/libpurple/tests/Makefile.am b/libpurple/tests/Makefile.am
index 1e48399821..ac1423ac79 100644
--- a/libpurple/tests/Makefile.am
+++ b/libpurple/tests/Makefile.am
@@ -10,6 +10,7 @@ check_libpurple_SOURCES=\
check_libpurple.c \
tests.h \
test_cipher.c \
+ test_jabber_caps.c \
test_jabber_jutil.c \
test_jabber_scram.c \
test_qq.c \
diff --git a/libpurple/tests/check_libpurple.c b/libpurple/tests/check_libpurple.c
index fb6bb73849..836b0dfa9c 100644
--- a/libpurple/tests/check_libpurple.c
+++ b/libpurple/tests/check_libpurple.c
@@ -37,16 +37,22 @@ static PurpleEventLoopUiOps eventloop_ui_ops = {
static void
purple_check_init(void) {
- gchar *home_dir;
-
g_type_init();
purple_eventloop_set_ui_ops(&eventloop_ui_ops);
+#if 0
/* build our fake home directory */
- home_dir = g_build_path(G_DIR_SEPARATOR_S, BUILDDIR, "libpurple", "tests", "home", NULL);
- purple_util_set_user_dir(home_dir);
- g_free(home_dir);
+ {
+ gchar *home_dir;
+
+ home_dir = g_build_path(G_DIR_SEPARATOR_S, BUILDDIR, "libpurple", "tests", "home", NULL);
+ purple_util_set_user_dir(home_dir);
+ g_free(home_dir);
+ }
+#else
+ purple_util_set_user_dir("/dev/null");
+#endif
purple_core_init("check");
}
@@ -78,6 +84,7 @@ int main(void)
sr = srunner_create (master_suite());
srunner_add_suite(sr, cipher_suite());
+ srunner_add_suite(sr, jabber_caps_suite());
srunner_add_suite(sr, jabber_jutil_suite());
srunner_add_suite(sr, jabber_scram_suite());
srunner_add_suite(sr, qq_suite());
diff --git a/libpurple/tests/test_jabber_caps.c b/libpurple/tests/test_jabber_caps.c
new file mode 100644
index 0000000000..6af180b658
--- /dev/null
+++ b/libpurple/tests/test_jabber_caps.c
@@ -0,0 +1,54 @@
+#include <string.h>
+
+#include "tests.h"
+#include "../xmlnode.h"
+#include "../protocols/jabber/caps.h"
+
+START_TEST(test_parse_invalid)
+{
+ xmlnode *query;
+
+ fail_unless(NULL == jabber_caps_parse_client_info(NULL));
+
+ /* Something other than a disco#info query */
+ query = xmlnode_new("foo");
+ fail_unless(NULL == jabber_caps_parse_client_info(query));
+ xmlnode_free(query);
+
+ query = xmlnode_new("query");
+ fail_unless(NULL == jabber_caps_parse_client_info(query));
+ xmlnode_set_namespace(query, "jabber:iq:last");
+ fail_unless(NULL == jabber_caps_parse_client_info(query));
+ xmlnode_free(query);
+}
+END_TEST
+
+#define assert_caps_calculate_match(hash_func, hash, str) { \
+ xmlnode *query = xmlnode_from_str((str), -1); \
+ JabberCapsClientInfo *info = jabber_caps_parse_client_info(query); \
+ gchar *got_hash = jabber_caps_calculate_hash(info, (hash_func)); \
+ assert_string_equal_free((hash), got_hash); \
+}
+
+START_TEST(test_calculate_caps)
+{
+ assert_caps_calculate_match("sha1", "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
+
+Suite *
+jabber_caps_suite(void)
+{
+ Suite *s = suite_create("Jabber Caps Functions");
+
+ TCase *tc = tcase_create("Parsing invalid ndoes");
+ tcase_add_test(tc, test_parse_invalid);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("Calculating from XMLnode");
+ tcase_add_test(tc, test_calculate_caps);
+ suite_add_tcase(s, tc);
+
+ return s;
+}
diff --git a/libpurple/tests/test_jabber_scram.c b/libpurple/tests/test_jabber_scram.c
index 81b9f3b408..512bc05a29 100644
--- a/libpurple/tests/test_jabber_scram.c
+++ b/libpurple/tests/test_jabber_scram.c
@@ -89,11 +89,13 @@ START_TEST(test_mech)
"c=biws,r=H7yDYKAWBCrM2Fa5SxGa4iezFPVDPpDUcGxPkH3RzP,p=pXkak78EuwwOEwk2/h/OzD7NkEI=",
"v=ldX4EBNnOgDnNTOCmbSfBHAUCOs=");
+#ifdef USE_IDN
assert_successful_exchange("pass½word", "GNb2HsNI7VnTv8ABsE5AnY8W",
"n=paul,r=GNb2HsNI7VnTv8ABsE5AnY8W",
"r=GNb2HsNI7VnTv8ABsE5AnY8W/w/I3eRKM0I7jxFWOH,s=ysAriUjPzFqOXnMQ,i=4096",
"c=biws,r=GNb2HsNI7VnTv8ABsE5AnY8W/w/I3eRKM0I7jxFWOH,p=n/CtgdWjOYnLQ4m9Na+wPn9D2uY=",
"v=4TkZwKWy6JHNmrUbU2+IdAaXtos=");
+#endif
}
END_TEST
diff --git a/libpurple/tests/test_util.c b/libpurple/tests/test_util.c
index 530886c41e..d5d6a8631a 100644
--- a/libpurple/tests/test_util.c
+++ b/libpurple/tests/test_util.c
@@ -66,17 +66,70 @@ START_TEST(test_util_text_strip_mnemonic)
}
END_TEST
+/*
+ * Many of the valid and invalid email addresses lised below are from
+ * http://fightingforalostcause.net/misc/2006/compare-email-regex.php
+ */
+const char *valid_emails[] = {
+ "purple-devel@lists.sf.net",
+ "l3tt3rsAndNumb3rs@domain.com",
+ "has-dash@domain.com",
+ "hasApostrophe.o'leary@domain.org",
+ "uncommonTLD@domain.museum",
+ "uncommonTLD@domain.travel",
+ "uncommonTLD@domain.mobi",
+ "countryCodeTLD@domain.uk",
+ "countryCodeTLD@domain.rw",
+ "lettersInDomain@911.com",
+ "underscore_inLocal@domain.net",
+ "IPInsteadOfDomain@127.0.0.1",
+ /* "IPAndPort@127.0.0.1:25", */
+ "subdomain@sub.domain.com",
+ "local@dash-inDomain.com",
+ "dot.inLocal@foo.com",
+ "a@singleLetterLocal.org",
+ "singleLetterDomain@x.org",
+ "&*=?^+{}'~@validCharsInLocal.net",
+ "foor@bar.newTLD"
+};
+
+const char *invalid_emails[] = {
+ "purple-devel@@lists.sf.net",
+ "purple@devel@lists.sf.net",
+ "purple-devel@list..sf.net",
+ "purple-devel",
+ "purple-devel@",
+ "@lists.sf.net",
+ "totally bogus",
+ "missingDomain@.com",
+ "@missingLocal.org",
+ "missingatSign.net",
+ "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 */
+ /* "two..consecutiveDots@domain.com", */ /* I don't think this is invalid -- Stu */
+ "domainStartsWithDash@-domain.com",
+ "domainEndsWithDash@domain-.com",
+ /* "numbersInTLD@domain.c0m", */
+ /* "missingTLD@domain.", */ /* This certainly isn't invalid -- Stu */
+ "! \"#$%(),/;<>[]`|@invalidCharsInLocal.org",
+ "invalidCharsInDomain@! \"#$%(),/;<>_[]`|.org",
+ /* "local@SecondLevelDomainNamesAreInvalidIfTheyAreLongerThan64Charactersss.org" */
+};
+
START_TEST(test_util_email_is_valid)
{
- fail_unless(purple_email_is_valid("purple-devel@lists.sf.net"));
- fail_if(purple_email_is_valid("purple-devel@@lists.sf.net"));
- fail_if(purple_email_is_valid("purple@devel@lists.sf.net"));
- fail_if(purple_email_is_valid("purple-devel@list..sf.net"));
- fail_if(purple_email_is_valid("purple-devel"));
- fail_if(purple_email_is_valid("purple-devel@"));
- fail_if(purple_email_is_valid("@lists.sf.net"));
- fail_if(purple_email_is_valid(""));
- fail_if(purple_email_is_valid("totally bogus"));
+ size_t i;
+
+ for (i = 0; i < G_N_ELEMENTS(valid_emails); i++)
+ fail_unless(purple_email_is_valid(valid_emails[i]), "Email address was: %s", valid_emails[i]);
+
+ for (i = 0; i < G_N_ELEMENTS(invalid_emails); i++)
+ fail_if(purple_email_is_valid(invalid_emails[i]), "Email address was: %s", invalid_emails[i]);
}
END_TEST
@@ -121,6 +174,37 @@ START_TEST(test_markup_html_to_xhtml)
}
END_TEST
+START_TEST(test_utf8_strip_unprintables)
+{
+ fail_unless(NULL == purple_utf8_strip_unprintables(NULL));
+ /* invalid UTF-8 */
+#if 0
+ /* disabled because make check fails on an assertion */
+ fail_unless(NULL == purple_utf8_strip_unprintables("abc\x80\x7f"));
+#endif
+ /* \t, \n, \r, space */
+ assert_string_equal_free("ab \tcd\nef\r ", purple_utf8_strip_unprintables("ab \tcd\nef\r "));
+ /* ASCII control characters (stripped) */
+ assert_string_equal_free(" aaaa ", purple_utf8_strip_unprintables(
+ "\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0E\x0F\x10 aaaa "
+ "\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"));
+ /* Basic ASCII */
+ assert_string_equal_free("Foobar", purple_utf8_strip_unprintables("Foobar"));
+ /* 0xE000 - 0xFFFD (UTF-8 encoded) */
+ /* U+F1F7 */
+ assert_string_equal_free("aaaa\xef\x87\xb7", purple_utf8_strip_unprintables("aaaa\xef\x87\xb7"));
+#if 0
+ /* disabled because make check fails on an assertion */
+ /* U+DB80 (Private Use High Surrogate, First) -- should be stripped */
+ assert_string_equal_free("aaaa", purple_utf8_strip_unprintables("aaaa\xed\xa0\x80"));
+ /* U+FFFE (should be stripped) */
+ assert_string_equal_free("aaaa", purple_utf8_strip_unprintables("aaaa\xef\xbf\xbe"));
+#endif
+ /* U+FEFF (should not be stripped) */
+ assert_string_equal_free("aaaa\xef\xbb\xbf", purple_utf8_strip_unprintables("aaaa\xef\xbb\xbf"));
+}
+END_TEST
+
START_TEST(test_mime_decode_field)
{
gchar *result = purple_mime_decode_field("=?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?=");
@@ -168,6 +252,10 @@ util_suite(void)
tcase_add_test(tc, test_markup_html_to_xhtml);
suite_add_tcase(s, tc);
+ tc = tcase_create("Stripping Unparseables");
+ tcase_add_test(tc, test_utf8_strip_unprintables);
+ suite_add_tcase(s, tc);
+
tc = tcase_create("MIME");
tcase_add_test(tc, test_mime_decode_field);
suite_add_tcase(s, tc);
diff --git a/libpurple/tests/tests.h b/libpurple/tests/tests.h
index 40ba1864c8..d3cbdb2c37 100644
--- a/libpurple/tests/tests.h
+++ b/libpurple/tests/tests.h
@@ -9,6 +9,7 @@
/* remember to add the suite to the runner in check_libpurple.c */
Suite * master_suite(void);
Suite * cipher_suite(void);
+Suite * jabber_caps_suite(void);
Suite * jabber_jutil_suite(void);
Suite * jabber_scram_suite(void);
Suite * qq_suite(void);
diff --git a/libpurple/upnp.c b/libpurple/upnp.c
index f91a70247e..3a6b3ce5ff 100644
--- a/libpurple/upnp.c
+++ b/libpurple/upnp.c
@@ -403,6 +403,11 @@ upnp_parse_description_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
lookup_internal_ip();
}
+ if (dd->inpa > 0)
+ purple_input_remove(dd->inpa);
+ if (dd->tima > 0)
+ purple_timeout_remove(dd->tima);
+
g_free(dd);
}
@@ -506,6 +511,8 @@ purple_upnp_discover_timeout(gpointer data)
if (dd->inpa)
purple_input_remove(dd->inpa);
+ if (dd->tima > 0)
+ purple_timeout_remove(dd->tima);
dd->inpa = 0;
dd->tima = 0;
@@ -610,7 +617,7 @@ purple_upnp_discover_send_broadcast(UPnPDiscoveryData *dd)
/* We have already done all our retries. Make sure that the callback
* doesn't get called before the original function returns */
- purple_timeout_add(10, purple_upnp_discover_timeout, dd);
+ dd->tima = purple_timeout_add(10, purple_upnp_discover_timeout, dd);
}
void
@@ -647,7 +654,7 @@ purple_upnp_discover(PurpleUPnPCallback cb, gpointer cb_data)
"purple_upnp_discover(): Failed In sock creation\n");
/* Short circuit the retry attempts */
dd->retry_count = NUM_UDP_ATTEMPTS;
- purple_timeout_add(10, purple_upnp_discover_timeout, dd);
+ dd->tima = purple_timeout_add(10, purple_upnp_discover_timeout, dd);
return;
}
@@ -659,7 +666,7 @@ purple_upnp_discover(PurpleUPnPCallback cb, gpointer cb_data)
"purple_upnp_discover(): Failed In gethostbyname\n");
/* Short circuit the retry attempts */
dd->retry_count = NUM_UDP_ATTEMPTS;
- purple_timeout_add(10, purple_upnp_discover_timeout, dd);
+ dd->tima = purple_timeout_add(10, purple_upnp_discover_timeout, dd);
return;
}
@@ -919,16 +926,20 @@ void purple_upnp_cancel_port_mapping(UPnPMappingAddRemove *ar)
GSList *l;
/* Remove ar from discovery_callbacks if present; it was inserted after a cb.
- * The same cb may be in the list multple times, so be careful to remove the one assocaited with ar. */
- l = discovery_callbacks;
+ * The same cb may be in the list multiple times, so be careful to remove
+ * the one associated with ar. */
+ l = discovery_callbacks;
while (l)
{
- if (l->next && (l->next->data == ar)) {
- discovery_callbacks = g_slist_delete_link(discovery_callbacks, l->next);
+ GSList *next = l->next;
+
+ if (next && (next->data == ar)) {
+ discovery_callbacks = g_slist_delete_link(discovery_callbacks, next);
+ next = l->next;
discovery_callbacks = g_slist_delete_link(discovery_callbacks, l);
}
- l = l->next;
+ l = next;
}
if (ar->tima > 0)
diff --git a/libpurple/util.c b/libpurple/util.c
index 7704880cce..60320c9ffe 100644
--- a/libpurple/util.c
+++ b/libpurple/util.c
@@ -28,6 +28,7 @@
#include "core.h"
#include "debug.h"
#include "notify.h"
+#include "ntlm.h"
#include "prpl.h"
#include "prefs.h"
#include "util.h"
@@ -69,6 +70,7 @@ struct _PurpleUtilFetchUrlData
unsigned long data_len;
gssize max_len;
gboolean chunked;
+ PurpleAccount *account;
};
static char *custom_user_dir = NULL;
@@ -586,6 +588,7 @@ purple_utf8_strftime(const char *format, const struct tm *tm)
{
purple_debug_error("util", "Format conversion failed in purple_utf8_strftime(): %s\n", err->message);
g_error_free(err);
+ err = NULL;
locale = g_strdup(format);
}
@@ -1307,12 +1310,17 @@ struct purple_parse_tag {
#define ALLOW_TAG_ALT(x, y) if(!g_ascii_strncasecmp(c, "<" x " ", strlen("<" x " "))) { \
const char *o = c + strlen("<" x); \
const char *p = NULL, *q = NULL, *r = NULL; \
+ /* o = iterating over full tag \
+ * p = > (end of tag) \
+ * q = start of quoted bit \
+ * r = < inside tag \
+ */ \
GString *innards = g_string_new(""); \
while(o && *o) { \
if(!q && (*o == '\"' || *o == '\'') ) { \
q = o; \
} else if(q) { \
- if(*o == *q) { \
+ if(*o == *q) { /* end of quoted bit */ \
char *unescaped = g_strndup(q+1, o-q-1); \
char *escaped = g_markup_escape_text(unescaped, -1); \
g_string_append_printf(innards, "%c%s%c", *q, escaped, *q); \
@@ -1332,7 +1340,7 @@ struct purple_parse_tag {
} \
o++; \
} \
- if(p && !r) { \
+ if(p && !r) { /* got an end of tag and no other < earlier */\
if(*(p-1) != '/') { \
struct purple_parse_tag *pt = g_new0(struct purple_parse_tag, 1); \
pt->src_tag = x; \
@@ -1345,7 +1353,7 @@ struct purple_parse_tag {
xhtml = g_string_append_c(xhtml, '>'); \
} \
c = p + 1; \
- } else { \
+ } else { /* got end of tag with earlier < *or* didn't get anything */ \
if(xhtml) \
xhtml = g_string_append(xhtml, "&lt;"); \
if(plain) \
@@ -2047,6 +2055,45 @@ badentity(const char *c)
return FALSE;
}
+static const char *
+process_link(GString *ret,
+ const char *start, const char *c,
+ int matchlen,
+ const char *urlprefix,
+ int inside_paren)
+{
+ char *url_buf, *tmpurlbuf;
+ const char *t;
+
+ for (t = c;; t++) {
+ if (!badchar(*t) && !badentity(t))
+ continue;
+
+ if (t - c == matchlen)
+ break;
+
+ if (*t == ',' && *(t + 1) != ' ') {
+ continue;
+ }
+
+ if (t > start && *(t - 1) == '.')
+ t--;
+ if (t > start && *(t - 1) == ')' && inside_paren > 0)
+ t--;
+
+ url_buf = g_strndup(c, t - c);
+ tmpurlbuf = purple_unescape_html(url_buf);
+ g_string_append_printf(ret, "<A HREF=\"%s%s\">%s</A>",
+ urlprefix,
+ tmpurlbuf, url_buf);
+ g_free(tmpurlbuf);
+ g_free(url_buf);
+ return t;
+ }
+
+ return c;
+}
+
char *
purple_markup_linkify(const char *text)
{
@@ -2094,129 +2141,22 @@ purple_markup_linkify(const char *text)
break;
}
}
- } else if ((*c=='h') && (!g_ascii_strncasecmp(c, "http://", 7) ||
- (!g_ascii_strncasecmp(c, "https://", 8)))) {
- t = c;
- while (1) {
- if (badchar(*t) || badentity(t)) {
-
- if ((!g_ascii_strncasecmp(c, "http://", 7) && (t - c == 7)) ||
- (!g_ascii_strncasecmp(c, "https://", 8) && (t - c == 8))) {
- break;
- }
-
- if (*(t) == ',' && (*(t + 1) != ' ')) {
- t++;
- continue;
- }
-
- if (*(t - 1) == '.')
- t--;
- if ((*(t - 1) == ')' && (inside_paren > 0))) {
- t--;
- }
-
- url_buf = g_strndup(c, t - c);
- tmpurlbuf = purple_unescape_html(url_buf);
- g_string_append_printf(ret, "<A HREF=\"%s\">%s</A>",
- tmpurlbuf, url_buf);
- g_free(url_buf);
- g_free(tmpurlbuf);
- c = t;
- break;
- }
- t++;
-
- }
- } else if (!g_ascii_strncasecmp(c, "www.", 4) && (c == text || badchar(c[-1]) || badentity(c-1))) {
- if (c[4] != '.') {
- t = c;
- while (1) {
- if (badchar(*t) || badentity(t)) {
- if (t - c == 4) {
- break;
- }
-
- if (*(t) == ',' && (*(t + 1) != ' ')) {
- t++;
- continue;
- }
-
- if (*(t - 1) == '.')
- t--;
- if ((*(t - 1) == ')' && (inside_paren > 0))) {
- t--;
- }
- url_buf = g_strndup(c, t - c);
- tmpurlbuf = purple_unescape_html(url_buf);
- g_string_append_printf(ret,
- "<A HREF=\"http://%s\">%s</A>", tmpurlbuf,
- url_buf);
- g_free(url_buf);
- g_free(tmpurlbuf);
- c = t;
- break;
- }
- t++;
- }
- }
- } else if (!g_ascii_strncasecmp(c, "ftp://", 6) || !g_ascii_strncasecmp(c, "sftp://", 7)) {
- t = c;
- while (1) {
- if (badchar(*t) || badentity(t)) {
-
- if ((!g_ascii_strncasecmp(c, "ftp://", 6) && (t - c == 6)) ||
- (!g_ascii_strncasecmp(c, "sftp://", 7) && (t - c == 7))) {
- break;
- }
-
- if (*(t - 1) == '.')
- t--;
- if ((*(t - 1) == ')' && (inside_paren > 0))) {
- t--;
- }
- url_buf = g_strndup(c, t - c);
- tmpurlbuf = purple_unescape_html(url_buf);
- g_string_append_printf(ret, "<A HREF=\"%s\">%s</A>",
- tmpurlbuf, url_buf);
- g_free(url_buf);
- g_free(tmpurlbuf);
- c = t;
- break;
- }
- if (!t)
- break;
- t++;
-
- }
- } else if (!g_ascii_strncasecmp(c, "ftp.", 4) && (c == text || badchar(c[-1]) || badentity(c-1))) {
- if (c[4] != '.') {
- t = c;
- while (1) {
- if (badchar(*t) || badentity(t)) {
- if (t - c == 4) {
- break;
- }
- if (*(t - 1) == '.')
- t--;
- if ((*(t - 1) == ')' && (inside_paren > 0))) {
- t--;
- }
- url_buf = g_strndup(c, t - c);
- tmpurlbuf = purple_unescape_html(url_buf);
- g_string_append_printf(ret,
- "<A HREF=\"ftp://%s\">%s</A>", tmpurlbuf,
- url_buf);
- g_free(url_buf);
- g_free(tmpurlbuf);
- c = t;
- break;
- }
- if (!t)
- break;
- t++;
- }
- }
+ } else if (!g_ascii_strncasecmp(c, "http://", 7)) {
+ c = process_link(ret, text, c, 7, "", inside_paren);
+ } else if (!g_ascii_strncasecmp(c, "https://", 8)) {
+ c = process_link(ret, text, c, 8, "", inside_paren);
+ } else if (!g_ascii_strncasecmp(c, "ftp://", 6)) {
+ c = process_link(ret, text, c, 6, "", inside_paren);
+ } else if (!g_ascii_strncasecmp(c, "sftp://", 7)) {
+ c = process_link(ret, text, c, 7, "", inside_paren);
+ } else if (!g_ascii_strncasecmp(c, "file://", 7)) {
+ c = process_link(ret, text, c, 7, "", inside_paren);
+ } else if (!g_ascii_strncasecmp(c, "www.", 4) && c[4] != '.' && (c == text || badchar(c[-1]) || badentity(c-1))) {
+ c = process_link(ret, text, c, 4, "http://", inside_paren);
+ } else if (!g_ascii_strncasecmp(c, "ftp.", 4) && c[4] != '.' && (c == text || badchar(c[-1]) || badentity(c-1))) {
+ c = process_link(ret, text, c, 4, "ftp://", inside_paren);
+ } else if (!g_ascii_strncasecmp(c, "xmpp:", 5) && (c == text || badchar(c[-1]) || badentity(c-1))) {
+ c = process_link(ret, text, c, 5, "", inside_paren);
} else if (!g_ascii_strncasecmp(c, "mailto:", 7)) {
t = c;
while (1) {
@@ -2225,7 +2165,7 @@ purple_markup_linkify(const char *text)
if (t - c == 7) {
break;
}
- if (*(t - 1) == '.')
+ if (t > text && *(t - 1) == '.')
t--;
if ((d = strstr(c + 7, "?")) != NULL && d < t)
url_buf = g_strndup(c + 7, d - c - 7);
@@ -2245,43 +2185,7 @@ purple_markup_linkify(const char *text)
c = t;
break;
}
- if (!t)
- break;
- t++;
-
- }
- } else if ((*c=='x') && (!g_ascii_strncasecmp(c, "xmpp:", 5)) &&
- (c == text || badchar(c[-1]) || badentity(c-1))) {
- t = c;
- while (1) {
- if (badchar(*t) || badentity(t)) {
-
- if (t - c == 5) {
- break;
- }
-
- if (*(t) == ',' && (*(t + 1) != ' ')) {
- t++;
- continue;
- }
-
- if (*(t - 1) == '.')
- t--;
- if ((*(t - 1) == ')' && (inside_paren > 0))) {
- t--;
- }
-
- url_buf = g_strndup(c, t - c);
- tmpurlbuf = purple_unescape_html(url_buf);
- g_string_append_printf(ret, "<A HREF=\"%s\">%s</A>",
- tmpurlbuf, url_buf);
- g_free(url_buf);
- g_free(tmpurlbuf);
- c = t;
- break;
- }
t++;
-
}
} else if (c != text && (*c == '@')) {
int flag;
@@ -2968,22 +2872,86 @@ purple_running_osx(void)
#endif
}
+typedef union purple_sockaddr {
+ struct sockaddr sa;
+ struct sockaddr_in sa_in;
+#if defined(AF_INET6)
+ struct sockaddr_in6 sa_in6;
+#endif
+ struct sockaddr_storage sa_stor;
+} PurpleSockaddr;
+
char *
purple_fd_get_ip(int fd)
{
- struct sockaddr addr;
+ PurpleSockaddr addr;
socklen_t namelen = sizeof(addr);
- struct in_addr in;
+ int family;
g_return_val_if_fail(fd != 0, NULL);
- if (getsockname(fd, &addr, &namelen))
+ if (getsockname(fd, &(addr.sa), &namelen))
return NULL;
- in = ((struct sockaddr_in *)&addr)->sin_addr;
- return g_strdup(inet_ntoa(in));
+ family = addr.sa.sa_family;
+
+ if (family == AF_INET) {
+ return g_strdup(inet_ntoa(addr.sa_in.sin_addr));
+ }
+#if defined(AF_INET6) && defined(HAVE_INET_NTOP)
+ else if (family == AF_INET6) {
+ char host[INET6_ADDRSTRLEN];
+ const char *tmp;
+
+ tmp = inet_ntop(family, &(addr.sa_in6.sin6_addr), host, sizeof(host));
+ return g_strdup(tmp);
+ }
+#endif
+
+ return NULL;
}
+int
+purple_socket_get_family(int fd)
+{
+ PurpleSockaddr addr;
+ socklen_t len = sizeof(addr);
+
+ g_return_val_if_fail(fd >= 0, -1);
+
+ if (getsockname(fd, &(addr.sa), &len))
+ return -1;
+
+ return addr.sa.sa_family;
+}
+
+gboolean
+purple_socket_speaks_ipv4(int fd)
+{
+ int family;
+
+ g_return_val_if_fail(fd >= 0, FALSE);
+
+ family = purple_socket_get_family(fd);
+
+ switch (family) {
+ case AF_INET:
+ return TRUE;
+#if defined(IPV6_V6ONLY)
+ case AF_INET6:
+ {
+ int val = 0;
+ guint len = sizeof(val);
+
+ if (getsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, &len) != 0)
+ return FALSE;
+ return !val;
+ }
+#endif
+ default:
+ return FALSE;
+ }
+}
/**************************************************************************
* String Functions
@@ -3147,7 +3115,7 @@ purple_str_strip_char(char *text, char thechar)
if (text[i] != thechar)
text[j++] = text[i];
- text[j++] = '\0';
+ text[j] = '\0';
}
void
@@ -3455,7 +3423,7 @@ 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;
- char scan_info[255];
+ const char * scan_info;
char port_str[6];
int f;
const char *at, *slash;
@@ -3463,11 +3431,12 @@ purple_url_parse(const char *url, char **ret_host, int *ret_port,
char host[256], path[256], user[256], passwd[256];
int port = 0;
/* hyphen at end includes it in control set */
- static const char addr_ctrl[] = "A-Za-z0-9.-";
- static const char port_ctrl[] = "0-9";
- static const char page_ctrl[] = "A-Za-z0-9.~_/:*!@&%%?=+^-";
- static const char user_ctrl[] = "A-Za-z0-9.~_/*!&%%?=+^-";
- static const char passwd_ctrl[] = "A-Za-z0-9.~_/*!&%%?=+^-";
+
+#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);
@@ -3487,37 +3456,32 @@ purple_url_parse(const char *url, char **ret_host, int *ret_port,
/* Only care about @ char BEFORE the first / */
at = strchr(url, '@');
slash = strchr(url, '/');
- if ((at != NULL) &&
- (((slash != NULL) && (strlen(at) > strlen(slash))) ||
- (slash == NULL))) {
- g_snprintf(scan_info, sizeof(scan_info),
- "%%255[%s]:%%255[%s]^@", user_ctrl, passwd_ctrl);
+ 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 ) {
+ if (f == 1) {
/* No passwd, possibly just username supplied */
- g_snprintf(scan_info, sizeof(scan_info),
- "%%255[%s]^@", user_ctrl);
+ scan_info = "%255[" USER_CTRL "]^@";
f = sscanf(url, scan_info, user);
- *passwd = '\0';
}
url = at+1; /* move pointer after the @ char */
- } else {
- *user = '\0';
- *passwd = '\0';
}
- g_snprintf(scan_info, sizeof(scan_info),
- "%%255[%s]:%%5[%s]/%%255[%s]", addr_ctrl, port_ctrl, page_ctrl);
+ 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)
{
- g_snprintf(scan_info, sizeof(scan_info),
- "%%255[%s]/%%255[%s]",
- addr_ctrl, page_ctrl);
+ scan_info = "%255[" ADDR_CTRL "]/%255[" PAGE_CTRL "]";
f = sscanf(url, scan_info, host, path);
/* Use the default port */
if (is_https)
@@ -3541,6 +3505,12 @@ purple_url_parse(const char *url, char **ret_host, int *ret_port,
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
}
/**
@@ -3653,11 +3623,11 @@ parse_redirect(const char *data, size_t data_len,
if (purple_strcasestr(new_url, "https://") != NULL) {
gfud->is_ssl = TRUE;
- gfud->ssl_connection = purple_ssl_connect(NULL,
+ 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, NULL,
+ gfud->connect_data = purple_proxy_connect(NULL, gfud->account,
gfud->website.address, gfud->website.port,
url_fetch_connect_cb, gfud);
}
@@ -3949,36 +3919,68 @@ url_fetch_send_cb(gpointer data, gint source, PurpleInputCondition cond)
gfud = data;
- if (gfud->request == NULL)
- {
+ 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
*/
- if (gfud->user_agent) {
- gfud->request = g_strdup_printf(
- "GET %s%s HTTP/%s\r\n"
- "Connection: close\r\n"
- "User-Agent: %s\r\n"
- "Accept: */*\r\n"
- "Host: %s\r\n\r\n",
- (gfud->full ? "" : "/"),
- (gfud->full ? (gfud->url ? gfud->url : "") : (gfud->website.page ? gfud->website.page : "")),
- (gfud->http11 ? "1.1" : "1.0"),
- (gfud->user_agent ? gfud->user_agent : ""),
- (gfud->website.address ? gfud->website.address : ""));
- } else {
- gfud->request = g_strdup_printf(
- "GET %s%s HTTP/%s\r\n"
- "Connection: close\r\n"
- "Accept: */*\r\n"
- "Host: %s\r\n\r\n",
- (gfud->full ? "" : "/"),
- (gfud->full ? (gfud->url ? gfud->url : "") : (gfud->website.page ? gfud->website.page : "")),
- (gfud->http11 ? "1.1" : "1.0"),
- (gfud->website.address ? gfud->website.address : ""));
+ 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())
@@ -4117,6 +4119,7 @@ purple_util_fetch_url_request_len_with_account(PurpleAccount *account,
gfud->include_headers = include_headers;
gfud->fd = -1;
gfud->max_len = max_len;
+ gfud->account = account;
purple_url_parse(url, &gfud->website.address, &gfud->website.port,
&gfud->website.page, &gfud->website.user, &gfud->website.passwd);
@@ -4270,6 +4273,8 @@ purple_email_is_valid(const char *address)
g_return_val_if_fail(address != NULL, FALSE);
+ if (*address == '.') return FALSE;
+
/* first we validate the name portion (name@domain) (rfc822)*/
for (c = address; *c; c++) {
if (*c == '\"' && (c == address || *(c - 1) == '.' || *(c - 1) == '\"')) {
@@ -4303,7 +4308,7 @@ purple_email_is_valid(const char *address)
do {
if (*c == '.' && (c == domain || *(c - 1) == '.' || *(c - 1) == '-'))
return FALSE;
- if (*c == '-' && *(c - 1) == '.') return FALSE;
+ if (*c == '-' && (*(c - 1) == '.' || *(c - 1) == '@')) return FALSE;
if ((*c < '0' && *c != '-' && *c != '.') || (*c > '9' && *c < 'A') ||
(*c > 'Z' && *c < 'a') || (*c > 'z')) return FALSE;
} while (*++c);
@@ -4528,12 +4533,22 @@ purple_utf8_strip_unprintables(const gchar *str)
}
workstr = iter = g_new(gchar, strlen(str) + 1);
- for ( ; *str; ++str) {
- guchar c = *str;
- if (c >= 0x20 || c == '\t' || c == '\n' || c == '\r') {
- *iter = c;
- ++iter;
+ while (*str) {
+ gunichar ch = g_utf8_get_char(str);
+ gchar *next = g_utf8_next_char(str);
+ /*
+ * Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] |
+ * [#x10000-#x10FFFF]
+ */
+ if ((ch == '\t' || ch == '\n' || ch == '\r') ||
+ (ch >= 0x20 && ch <= 0xD7FF) ||
+ (ch >= 0xE000 && ch <= 0xFFFD) ||
+ (ch >= 0x10000 && ch <= 0x10FFFF)) {
+ memcpy(iter, str, next - str);
+ iter += (next - str);
}
+
+ str = next;
}
/* nul-terminate the new string */
@@ -4880,7 +4895,13 @@ purple_escape_filename(const char *str)
}
}
}
-
+#ifdef _WIN32
+ /* File/Directory names in windows cannot end in periods/spaces.
+ * http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx
+ */
+ while (j > 0 && (buf[j - 1] == '.' || buf[j - 1] == ' '))
+ j--;
+#endif
buf[j] = '\0';
return buf;
diff --git a/libpurple/util.h b/libpurple/util.h
index 357ae7e5ea..282c4bbfcd 100644
--- a/libpurple/util.h
+++ b/libpurple/util.h
@@ -819,6 +819,29 @@ gboolean purple_running_osx(void);
*/
char *purple_fd_get_ip(int fd);
+/**
+ * Returns the address family of a socket.
+ *
+ * @param fd The socket file descriptor.
+ *
+ * @return 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);
+
+/**
+ * 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
+ */
+gboolean purple_socket_speaks_ipv4(int fd);
+
/*@}*/
diff --git a/libpurple/win32/global.mak b/libpurple/win32/global.mak
index 979a891fff..4351f3d989 100644
--- a/libpurple/win32/global.mak
+++ b/libpurple/win32/global.mak
@@ -37,7 +37,6 @@ PURPLE_TOP := $(PIDGIN_TREE_TOP)/libpurple
PURPLE_PLUGINS_TOP := $(PURPLE_TOP)/plugins
PURPLE_PERL_TOP := $(PURPLE_PLUGINS_TOP)/perl
PIDGIN_TOP := $(PIDGIN_TREE_TOP)/pidgin
-PIDGIN_IDLETRACK_TOP := $(PIDGIN_TOP)/win32/IdleTracker
PIDGIN_PIXMAPS_TOP := $(PIDGIN_TOP)/pixmaps
PIDGIN_PLUGINS_TOP := $(PIDGIN_TOP)/plugins
PURPLE_PO_TOP := $(PIDGIN_TREE_TOP)/po
@@ -48,7 +47,6 @@ PIDGIN_CONFIG_H := $(PIDGIN_TREE_TOP)/config.h
PURPLE_CONFIG_H := $(PIDGIN_TREE_TOP)/config.h
PIDGIN_REVISION_H := $(PIDGIN_TREE_TOP)/package_revision.h
PIDGIN_REVISION_RAW_TXT := $(PIDGIN_TREE_TOP)/package_revision_raw.txt
-PIDGIN_IDLETRACK_DLL := $(PIDGIN_IDLETRACK_TOP)/idletrack.dll
PURPLE_PURPLE_H := $(PURPLE_TOP)/purple.h
PURPLE_VERSION_H := $(PURPLE_TOP)/version.h
PURPLE_DLL := $(PURPLE_TOP)/libpurple.dll
@@ -108,6 +106,7 @@ MAKENSISOPT ?= /
PERL ?= perl
WINDRES ?= windres
STRIP ?= strip
+INTLTOOL_MERGE ?= $(WIN32_DEV_TOP)/intltool_0.40.4-1_win32/bin/intltool-merge
PIDGIN_COMMON_RULES := $(PURPLE_TOP)/win32/rules.mak
PIDGIN_COMMON_TARGETS := $(PURPLE_TOP)/win32/targets.mak
diff --git a/libpurple/win32/rules.mak b/libpurple/win32/rules.mak
index 7d1962154f..f372f2a5be 100644
--- a/libpurple/win32/rules.mak
+++ b/libpurple/win32/rules.mak
@@ -8,3 +8,6 @@
%.o: %.rc
$(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 $< $@
diff --git a/libpurple/win32/targets.mak b/libpurple/win32/targets.mak
index 89b912d5f6..e31c2f424a 100644
--- a/libpurple/win32/targets.mak
+++ b/libpurple/win32/targets.mak
@@ -36,9 +36,6 @@ $(PURPLE_PERL_DLL) $(PURPLE_PERL_DLL).a:
$(PIDGIN_DLL) $(PIDGIN_DLL).a:
$(MAKE) -C $(PIDGIN_TOP) -f $(MINGW_MAKEFILE) pidgin.dll
-$(PIDGIN_IDLETRACK_DLL) $(PIDGIN_IDLETRACK_DLL).a:
- $(MAKE) -C $(PIDGIN_IDLETRACK_TOP) -f $(MINGW_MAKEFILE) idletrack.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 d2740dc15f..2c2824f744 100644
--- a/libpurple/win32/win32dep.c
+++ b/libpurple/win32/win32dep.c
@@ -35,7 +35,7 @@
static char *app_data_dir = NULL, *install_dir = NULL,
*lib_dir = NULL, *locale_dir = NULL;
-static HINSTANCE libpurpledll_hInstance = 0;
+static HINSTANCE libpurpledll_hInstance = NULL;
/*
* PUBLIC CODE
@@ -77,16 +77,23 @@ FARPROC wpurple_find_and_loadproc(const char *dllname, const char *procedure) {
BOOL did_load = FALSE;
FARPROC proc = 0;
- if(!(hmod = GetModuleHandle(dllname))) {
+ 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(!(hmod = LoadLibrary(dllname))) {
- purple_debug_error("wpurple", "Could not load: %s\n", dllname);
+ if(!(hmod = LoadLibraryW(wc_dllname))) {
+ purple_debug_error("wpurple", "Could not load: %s (%s)\n", dllname,
+ g_win32_error_message(GetLastError()));
+ g_free(wc_dllname);
return NULL;
}
else
did_load = TRUE;
}
+ g_free(wc_dllname);
+ wc_dllname = NULL;
+
if((proc = GetProcAddress(hmod, procedure))) {
purple_debug_info("wpurple", "This version of %s contains %s\n",
dllname, procedure);
@@ -124,7 +131,7 @@ const char *wpurple_install_dir(void) {
if (!initialized) {
char *tmp = NULL;
wchar_t winstall_dir[MAXPATHLEN];
- if (GetModuleFileNameW(NULL, winstall_dir,
+ if (GetModuleFileNameW(libpurpledll_hInstance, winstall_dir,
MAXPATHLEN) > 0) {
tmp = g_utf16_to_utf8(winstall_dir, -1,
NULL, NULL, NULL);
diff --git a/libpurple/xmlnode.c b/libpurple/xmlnode.c
index eeab70c4c5..9e24663928 100644
--- a/libpurple/xmlnode.c
+++ b/libpurple/xmlnode.c
@@ -223,7 +223,7 @@ xmlnode_set_attrib_full(xmlnode *node, const char *attr, const char *xmlns, cons
const char *
-xmlnode_get_attrib(xmlnode *node, const char *attr)
+xmlnode_get_attrib(const xmlnode *node, const char *attr)
{
xmlnode *x;
@@ -240,9 +240,9 @@ xmlnode_get_attrib(xmlnode *node, const char *attr)
}
const char *
-xmlnode_get_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns)
+xmlnode_get_attrib_with_namespace(const xmlnode *node, const char *attr, const char *xmlns)
{
- xmlnode *x;
+ const xmlnode *x;
g_return_val_if_fail(node != NULL, NULL);
g_return_val_if_fail(attr != NULL, NULL);
diff --git a/libpurple/xmlnode.h b/libpurple/xmlnode.h
index 737bab8443..95b330cde1 100644
--- a/libpurple/xmlnode.h
+++ b/libpurple/xmlnode.h
@@ -205,7 +205,7 @@ void xmlnode_set_attrib_full(xmlnode *node, const char *attr, const char *xmlns,
*
* @return The value of the attribute.
*/
-const char *xmlnode_get_attrib(xmlnode *node, const char *attr);
+const char *xmlnode_get_attrib(const xmlnode *node, const char *attr);
/**
* Gets a namespaced attribute from a node
@@ -216,7 +216,7 @@ const char *xmlnode_get_attrib(xmlnode *node, const char *attr);
*
* @return The value of the attribute/
*/
-const char *xmlnode_get_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns);
+const char *xmlnode_get_attrib_with_namespace(const xmlnode *node, const char *attr, const char *xmlns);
/**
* Removes an attribute from a node.