diff options
Diffstat (limited to 'libpurple')
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 ߪ 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,"<desc>")) != NULL) { - temp=strstr(xml,"</desc>"); - temp[0]=0; - desc=desc+12; - } - if ((title=strstr(xml,"<title>")) != NULL) { - temp=strstr(xml,"</title>"); - 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, "<title>"); + if (tmp1 != NULL) { + tmp1 += 13; + tmp2 = strstr(tmp1, "</title>"); + if (tmp2 != NULL) + g_string_append_len(xstatus, tmp1, tmp2 - tmp1); + } + tmp1 = strstr(xml, "<desc>"); + if (tmp1 != NULL) { + tmp1 += 12; + tmp2 = strstr(tmp1, "</desc>"); + 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><Q><PluginID>srvMng</PluginID></Q></QUERY><NOTIFY><srv><id>cAwaySrv</id><req><id>AwayStat</id><trans>2</trans><senderId>%s</senderId></req></srv></NOTIFY></N>\r\n"; + fmt = "<N><QUERY><Q><PluginID>srvMng</PluginID></Q></QUERY><NOTIFY><srv><id>cAwaySrv</id><req><id>AwayStat</id><trans>2</trans><senderId>%s</senderId></req></srv></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><ret event='OnRemoteNotification'><srv><id>cAwaySrv</id><val srv_id='cAwaySrv'><Root><CASXtraSetAwayMessage></CASXtraSetAwayMessage>&l t;uin>%s</uin><index>1</index><title>%s</title><desc>%s</desc></Root></val></srv><srv><id>cRandomizerSrv</id><val srv_id='cRandomizerSrv'>undefined</val></srv></ret></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, "<"); \ 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. |