Fri, 25 Oct 2013 00:31:39 +0530
Merged default branch
--- a/libpurple/Makefile.am Thu Oct 24 20:24:29 2013 +0530 +++ b/libpurple/Makefile.am Fri Oct 25 00:31:39 2013 +0530 @@ -74,6 +74,7 @@ core.c \ debug.c \ desktopitem.c \ + e2ee.c \ eventloop.c \ http.c \ idle.c \ @@ -149,6 +150,7 @@ dbus-maybe.h \ debug.h \ desktopitem.h \ + e2ee.h \ eventloop.h \ http.h \ idle.h \
--- a/libpurple/conversation.c Thu Oct 24 20:24:29 2013 +0530 +++ b/libpurple/conversation.c Fri Oct 25 00:31:39 2013 +0530 @@ -57,6 +57,8 @@ PurpleConnectionFlags features; /**< The supported features */ GList *message_history; /**< Message history, as a GList of PurpleConversationMessage's */ + + PurpleE2eeState *e2ee_state; /**< End-to-end encryption state. */ }; /** @@ -440,6 +442,55 @@ } void +purple_conversation_set_e2ee_state(PurpleConversation *conv, + PurpleE2eeState *state) +{ + PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv); + + g_return_if_fail(priv != NULL); + + if (state != NULL && purple_e2ee_state_get_provider(state) != + purple_e2ee_provider_get_main()) + { + purple_debug_error("conversation", + "This is not the main e2ee provider"); + + return; + } + + if (state) + purple_e2ee_state_ref(state); + purple_e2ee_state_unref(priv->e2ee_state); + priv->e2ee_state = state; + + purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_E2EE); +} + +PurpleE2eeState * +purple_conversation_get_e2ee_state(PurpleConversation *conv) +{ + PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv); + PurpleE2eeProvider *provider; + + g_return_val_if_fail(priv != NULL, NULL); + + if (priv->e2ee_state == NULL) + return NULL; + + provider = purple_e2ee_provider_get_main(); + if (provider == NULL) + return NULL; + + if (purple_e2ee_state_get_provider(priv->e2ee_state) != provider) { + purple_debug_warning("conversation", + "e2ee state has invalid provider set"); + return NULL; + } + + return priv->e2ee_state; +} + +void purple_conversation_set_logging(PurpleConversation *conv, gboolean log) { PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv); @@ -1025,6 +1076,9 @@ purple_request_close_with_handle(conv); + purple_e2ee_state_unref(priv->e2ee_state); + priv->e2ee_state = NULL; + /* remove from conversations and im/chats lists prior to emit */ purple_conversations_remove(conv);
--- a/libpurple/conversation.h Thu Oct 24 20:24:29 2013 +0530 +++ b/libpurple/conversation.h Fri Oct 25 00:31:39 2013 +0530 @@ -64,6 +64,8 @@ PURPLE_CONVERSATION_UPDATE_LOGGING, /**< Logging for this conversation was enabled or disabled. */ PURPLE_CONVERSATION_UPDATE_TOPIC, /**< The topic for a chat was updated. */ + PURPLE_CONVERSATION_UPDATE_E2EE, /**< The End-to-end encryption state was + updated. */ /* * XXX These need to go when we implement a more generic core/UI event * system. @@ -155,6 +157,7 @@ #include "account.h" #include "buddyicon.h" +#include "e2ee.h" #include "log.h" /**************************************************************************/ @@ -379,6 +382,26 @@ const char *purple_conversation_get_name(const PurpleConversation *conv); /** + * Sets current E2EE state for the conversation. + * + * @param conv The conversation. + * @param state The E2EE state. + */ +void +purple_conversation_set_e2ee_state(PurpleConversation *conv, + PurpleE2eeState *state); + +/** + * Gets current conversation's E2EE state. + * + * @param conv The conversation. + * + * @return Current E2EE state for conversation. + */ +PurpleE2eeState * +purple_conversation_get_e2ee_state(PurpleConversation *conv); + +/** * Enables or disables logging for this conversation. * * @param conv The conversation.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/e2ee.c Fri Oct 25 00:31:39 2013 +0530 @@ -0,0 +1,225 @@ +/** + * @file e2ee.c End-to-end encryption API + * @ingroup core + */ + +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "e2ee.h" + +#include "debug.h" + +struct _PurpleE2eeState +{ + PurpleE2eeProvider *provider; + + gchar *name; + gchar *stock_icon; + + guint ref_count; +}; + +struct _PurpleE2eeProvider +{ + gchar *name; + PurpleE2eeConvMenuCallback conv_menu_cb; +}; + +static PurpleE2eeProvider *main_provider = NULL; + +/*** Encryption states for conversations. *************************************/ + +PurpleE2eeState * +purple_e2ee_state_new(PurpleE2eeProvider *provider) +{ + PurpleE2eeState *state; + + g_return_val_if_fail(provider != NULL, NULL); + + state = g_new0(PurpleE2eeState, 1); + state->provider = provider; + state->ref_count = 1; + + return state; +} + +void +purple_e2ee_state_ref(PurpleE2eeState *state) +{ + g_return_if_fail(state != NULL); + + state->ref_count++; +} + +PurpleE2eeState * +purple_e2ee_state_unref(PurpleE2eeState *state) +{ + if (state == NULL) + return NULL; + + state->ref_count--; + if (state->ref_count > 0) + return state; + + g_free(state->name); + g_free(state->stock_icon); + g_free(state); + + return NULL; +} + +PurpleE2eeProvider * +purple_e2ee_state_get_provider(PurpleE2eeState *state) +{ + g_return_val_if_fail(state != NULL, NULL); + + return state->provider; +} + +void +purple_e2ee_state_set_name(PurpleE2eeState *state, const gchar *name) +{ + g_return_if_fail(state != NULL); + g_return_if_fail(name != NULL); + + g_free(state->name); + state->name = g_strdup(name); +} + +const gchar * +purple_e2ee_state_get_name(PurpleE2eeState *state) +{ + g_return_val_if_fail(state != NULL, NULL); + + return state->name; +} + +void +purple_e2ee_state_set_stock_icon(PurpleE2eeState *state, + const gchar *stock_icon) +{ + g_return_if_fail(state != NULL); + g_return_if_fail(stock_icon != NULL); + + g_free(state->stock_icon); + state->stock_icon = g_strdup(stock_icon); +} + +const gchar * +purple_e2ee_state_get_stock_icon(PurpleE2eeState *state) +{ + g_return_val_if_fail(state, NULL); + + return state->stock_icon; +} + +/*** Encryption providers API. ************************************************/ + +PurpleE2eeProvider * +purple_e2ee_provider_new(void) +{ + PurpleE2eeProvider *provider; + + provider = g_new0(PurpleE2eeProvider, 1); + + return provider; +} + +void +purple_e2ee_provider_free(PurpleE2eeProvider *provider) +{ + g_return_if_fail(provider != NULL); + + if (provider == main_provider) { + purple_debug_error("e2ee", "This provider is still registered"); + return; + } + + g_free(provider->name); + g_free(provider); +} + +gboolean +purple_e2ee_provider_register(PurpleE2eeProvider *provider) +{ + g_return_val_if_fail(provider != NULL, FALSE); + + if (main_provider != NULL) + return FALSE; + + main_provider = provider; + return TRUE; +} + +void +purple_e2ee_provider_unregister(PurpleE2eeProvider *provider) +{ + g_return_if_fail(provider != NULL); + + if (main_provider != provider) { + purple_debug_warning("e2ee", "This provider is not registered"); + return; + } + + main_provider = NULL; +} + +PurpleE2eeProvider * +purple_e2ee_provider_get_main(void) +{ + return main_provider; +} + +void +purple_e2ee_provider_set_name(PurpleE2eeProvider *provider, const gchar *name) +{ + g_return_if_fail(provider != NULL); + g_return_if_fail(name != NULL); + + g_free(provider->name); + provider->name = g_strdup(name); +} + +const gchar * +purple_e2ee_provider_get_name(PurpleE2eeProvider *provider) +{ + g_return_val_if_fail(provider != NULL, NULL); + + return provider->name; +} + +void +purple_e2ee_provider_set_conv_menu_cb(PurpleE2eeProvider *provider, + PurpleE2eeConvMenuCallback conv_menu_cb) +{ + g_return_if_fail(provider != NULL); + + provider->conv_menu_cb = conv_menu_cb; +} + +PurpleE2eeConvMenuCallback +purple_e2ee_provider_get_conv_menu_cb(PurpleE2eeProvider *provider) +{ + g_return_val_if_fail(provider != NULL, NULL); + + return provider->conv_menu_cb; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/e2ee.h Fri Oct 25 00:31:39 2013 +0530 @@ -0,0 +1,231 @@ +/** + * @file e2ee.h End-to-end encryption API + * @ingroup core + */ + +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef _PURPLE_E2EE_H_ +#define _PURPLE_E2EE_H_ + +typedef struct _PurpleE2eeState PurpleE2eeState; + +typedef struct _PurpleE2eeProvider PurpleE2eeProvider; + +#include <glib.h> +#include "conversation.h" + +typedef GList * (*PurpleE2eeConvMenuCallback)(PurpleConversation *conv); + +G_BEGIN_DECLS + +/**************************************************************************/ +/** @name Encryption states for conversations. */ +/**************************************************************************/ +/*@{*/ + +/** + * Creates new E2EE state. + * + * State objects are global (shared between multiple conversations). + * + * @param provider The E2EE provider that created this state. + * + * @return New E2EE state. + */ +PurpleE2eeState * +purple_e2ee_state_new(PurpleE2eeProvider *provider); + +/** + * Increment the reference count. + * + * @param state The E2EE state. + */ +void +purple_e2ee_state_ref(PurpleE2eeState *state); + +/** + * Decrement the reference count. + * + * If the reference count reaches zero, the state will be freed. + * + * @param state The E2EE state. + * + * @return @a state or @c NULL if the reference count reached zero. + */ +PurpleE2eeState * +purple_e2ee_state_unref(PurpleE2eeState *state); + +/** + * Gets the provider of specified E2EE state. + * + * @param state The E2EE state. + * + * @return The provider for this state. + */ +PurpleE2eeProvider * +purple_e2ee_state_get_provider(PurpleE2eeState *state); + +/** + * Sets the name for the E2EE state. + * + * @param state The E2EE state. + * @param name The localized name. + */ +void +purple_e2ee_state_set_name(PurpleE2eeState *state, const gchar *name); + +/** + * Gets the name of the E2EE state. + * + * @param state The E2EE state. + * + * @return The localized name. + */ +const gchar * +purple_e2ee_state_get_name(PurpleE2eeState *state); + +/** + * Sets the icon for the E2EE state. + * + * @param state The E2EE state. + * @param stock_icon The stock icon identifier. + */ +void +purple_e2ee_state_set_stock_icon(PurpleE2eeState *state, + const gchar *stock_icon); + +/** + * Gets the icon of the E2EE state. + * + * @param state The E2EE state. + * + * @return The stock icon identifier. + */ +const gchar * +purple_e2ee_state_get_stock_icon(PurpleE2eeState *state); + +/*@}*/ + + +/**************************************************************************/ +/** @name Encryption providers API. */ +/**************************************************************************/ +/*@{*/ + +/** + * Creates new E2EE provider. + * + * @return New E2EE provider. + */ +PurpleE2eeProvider * +purple_e2ee_provider_new(void); + +/** + * Destroys the E2EE provider. + * + * The provider have to be unregistered prior. + * + * @param provider The provider. + */ +void +purple_e2ee_provider_free(PurpleE2eeProvider *provider); + +/** + * Registers the E2EE provider. + * + * Currently, there is no support for multiple E2EE providers - only the first + * one is registered. + * + * @param provider The E2EE provider. + * + * @return @c TRUE, if the provider was successfully registered, + * @c FALSE otherwise. + */ +gboolean +purple_e2ee_provider_register(PurpleE2eeProvider *provider); + +/** + * Unregisters the E2EE provider. + * + * @param provider The E2EE provider. + */ +void +purple_e2ee_provider_unregister(PurpleE2eeProvider *provider); + +/** + * Gets main E2EE provider. + * + * @return The main E2EE provider. + */ +PurpleE2eeProvider * +purple_e2ee_provider_get_main(void); + +/** + * Sets the name for the E2EE provider. + * + * @param provider The E2EE provider. + * @param name The localized name. + */ +void +purple_e2ee_provider_set_name(PurpleE2eeProvider *provider, const gchar *name); + +/** + * Gets the name of the E2EE provider. + * + * @param provider The E2EE provider. + * + * @return The localized name of specified E2EE provider. + */ +const gchar * +purple_e2ee_provider_get_name(PurpleE2eeProvider *provider); + +/** + * Sets the conversation menu callback for the E2EE provider. + * + * The function is called, when user extends the E2EE menu for the conversation + * specified in its parameter. + * + * Function should return the GList of PurpleMenuAction objects. + * + * @param provider The E2EE provider. + * @param conv_menu_cb The callback. + */ +void +purple_e2ee_provider_set_conv_menu_cb(PurpleE2eeProvider *provider, + PurpleE2eeConvMenuCallback conv_menu_cb); + +/** + * Gets the conversation menu callback of the E2EE provider. + * + * @param provider The E2EE provider. + * + * @return The callback. + */ +PurpleE2eeConvMenuCallback +purple_e2ee_provider_get_conv_menu_cb(PurpleE2eeProvider *provider); + +/*@}*/ + +G_END_DECLS + +#endif /* _PURPLE_E2EE_H_ */
--- a/libpurple/imgstore.h Thu Oct 24 20:24:29 2013 +0530 +++ b/libpurple/imgstore.h Fri Oct 25 00:31:39 2013 +0530 @@ -31,6 +31,7 @@ #include <glib.h> #define PURPLE_STORED_IMAGE_PROTOCOL "purple-image:" +#define PURPLE_STOCK_IMAGE_PROTOCOL "purple-stock-image:" /** A reference-counted immutable wrapper around an image's data and its * filename.
--- a/libpurple/plugins/perl/common/Request.xs Thu Oct 24 20:24:29 2013 +0530 +++ b/libpurple/plugins/perl/common/Request.xs Fri Oct 25 00:31:39 2013 +0530 @@ -487,10 +487,6 @@ Purple::Request::Field field gboolean -purple_request_field_string_is_editable(field) - Purple::Request::Field field - -gboolean purple_request_field_string_is_masked(field) Purple::Request::Field field @@ -504,11 +500,6 @@ const char *default_value void -purple_request_field_string_set_editable(field, editable) - Purple::Request::Field field - gboolean editable - -void purple_request_field_string_set_masked(field, masked) Purple::Request::Field field gboolean masked
--- a/libpurple/protocols/mxit/actions.c Thu Oct 24 20:24:29 2013 +0530 +++ b/libpurple/protocols/mxit/actions.c Fri Oct 25 00:31:39 2013 +0530 @@ -239,7 +239,7 @@ field = purple_request_field_string_new( "bday", _( "Birthday" ), profile->birthday, FALSE ); purple_request_field_group_add_field( public_group, field ); if ( profile->flags & CP_PROF_DOBLOCKED ) - purple_request_field_string_set_editable( field, FALSE ); + purple_request_field_set_sensitive( field, FALSE ); /* gender */ field = purple_request_field_choice_new( "male", _( "Gender" ), GINT_TO_POINTER(profile->male ? 1 : 0));
--- a/libpurple/protocols/mxit/login.c Thu Oct 24 20:24:29 2013 +0530 +++ b/libpurple/protocols/mxit/login.c Fri Oct 25 00:31:39 2013 +0530 @@ -338,7 +338,7 @@ /* mxit login name */ field = purple_request_field_string_new( "loginname", _( "MXit ID" ), purple_account_get_username( session->acc ), FALSE ); - purple_request_field_string_set_editable( field, FALSE ); + purple_request_field_set_sensitive( field, FALSE ); purple_request_field_group_add_field( group, field ); /* nick name (required) */
--- a/libpurple/request.c Thu Oct 24 20:24:29 2013 +0530 +++ b/libpurple/request.c Fri Oct 25 00:31:39 2013 +0530 @@ -59,6 +59,8 @@ gboolean visible; gboolean required; + gboolean sensitive; + PurpleRequestFieldSensitivityCb sensitivity_cb; union { @@ -66,7 +68,6 @@ { gboolean multiline; gboolean masked; - gboolean editable; char *default_value; char *value; @@ -150,6 +151,8 @@ GList *validated_fields; + GList *autosensitive_fields; + void *ui_data; }; @@ -185,6 +188,9 @@ gpointer parent_from; }; +static void +purple_request_fields_check_others_sensitivity(PurpleRequestField *field); + PurpleRequestCommonParameters * purple_request_cpar_new(void) { @@ -492,6 +498,7 @@ g_list_free(fields->groups); g_list_free(fields->required_fields); g_list_free(fields->validated_fields); + g_list_free(fields->autosensitive_fields); g_hash_table_destroy(fields->fields); g_free(fields); } @@ -529,6 +536,10 @@ g_list_append(fields->validated_fields, field); } + if (field->sensitivity_cb != NULL) { + fields->autosensitive_fields = + g_list_append(fields->autosensitive_fields, field); + } } } @@ -565,6 +576,14 @@ return fields->validated_fields; } +const GList * +purple_request_fields_get_autosensitive(const PurpleRequestFields *fields) +{ + g_return_val_if_fail(fields != NULL, NULL); + + return fields->autosensitive_fields; +} + gboolean purple_request_fields_is_field_required(const PurpleRequestFields *fields, const char *id) @@ -633,6 +652,37 @@ return TRUE; } +static void +purple_request_fields_check_sensitivity(PurpleRequestFields *fields) +{ + GList *it; + + g_return_if_fail(fields != NULL); + + for (it = fields->autosensitive_fields; it; it = g_list_next(it)) { + PurpleRequestField *field = it->data; + + if (field->sensitivity_cb == NULL) { + g_warn_if_reached(); + continue; + } + + purple_request_field_set_sensitive(field, + field->sensitivity_cb(field)); + } +} + +static void +purple_request_fields_check_others_sensitivity(PurpleRequestField *field) +{ + g_return_if_fail(field != NULL); + + if (field->group == NULL || field->group->fields_list == NULL) + return; + + purple_request_fields_check_sensitivity(field->group->fields_list); +} + PurpleRequestField * purple_request_fields_get_field(const PurpleRequestFields *fields, const char *id) { @@ -779,12 +829,18 @@ group->fields_list->required_fields = g_list_append(group->fields_list->required_fields, field); } - + if (purple_request_field_is_validatable(field)) { group->fields_list->validated_fields = g_list_append(group->fields_list->validated_fields, field); } + + if (field->sensitivity_cb != NULL) + { + group->fields_list->autosensitive_fields = + g_list_append(group->fields_list->autosensitive_fields, field); + } } field->group = group; @@ -831,6 +887,7 @@ purple_request_field_set_label(field, text); purple_request_field_set_visible(field, TRUE); + purple_request_field_set_sensitive(field, TRUE); return field; } @@ -1082,6 +1139,45 @@ return valid; } +void +purple_request_field_set_sensitive(PurpleRequestField *field, + gboolean sensitive) +{ + g_return_if_fail(field != NULL); + + field->sensitive = sensitive; +} + +gboolean +purple_request_field_is_sensitive(PurpleRequestField *field) +{ + g_return_val_if_fail(field != NULL, FALSE); + + return field->sensitive; +} + +void +purple_request_field_set_sensitivity_cb(PurpleRequestField *field, + PurpleRequestFieldSensitivityCb cb) +{ + PurpleRequestFields *flist; + + g_return_if_fail(field != NULL); + + field->sensitivity_cb = cb; + + if (!field->group || !field->group->fields_list) + return; + flist = field->group->fields_list; + flist->autosensitive_fields = g_list_remove(flist->autosensitive_fields, + field); + if (cb != NULL) + { + flist->autosensitive_fields = g_list_append( + flist->autosensitive_fields, field); + } +} + PurpleRequestField * purple_request_field_string_new(const char *id, const char *text, const char *default_value, gboolean multiline) @@ -1094,7 +1190,6 @@ field = purple_request_field_new(id, text, PURPLE_REQUEST_FIELD_STRING); field->u.string.multiline = multiline; - field->u.string.editable = TRUE; purple_request_field_string_set_default_value(field, default_value); purple_request_field_string_set_value(field, default_value); @@ -1121,6 +1216,8 @@ g_free(field->u.string.value); field->u.string.value = g_strdup(value); + + purple_request_fields_check_others_sensitivity(field); } void @@ -1132,16 +1229,6 @@ field->u.string.masked = masked; } -void -purple_request_field_string_set_editable(PurpleRequestField *field, - gboolean editable) -{ - g_return_if_fail(field != NULL); - g_return_if_fail(field->type == PURPLE_REQUEST_FIELD_STRING); - - field->u.string.editable = editable; -} - const char * purple_request_field_string_get_default_value(const PurpleRequestField *field) { @@ -1178,15 +1265,6 @@ return field->u.string.masked; } -gboolean -purple_request_field_string_is_editable(const PurpleRequestField *field) -{ - g_return_val_if_fail(field != NULL, FALSE); - g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_STRING, FALSE); - - return field->u.string.editable; -} - PurpleRequestField * purple_request_field_int_new(const char *id, const char *text, int default_value, int lower_bound, int upper_bound) @@ -1249,6 +1327,8 @@ } field->u.integer.value = value; + + purple_request_fields_check_others_sensitivity(field); } int @@ -1321,6 +1401,8 @@ g_return_if_fail(field->type == PURPLE_REQUEST_FIELD_BOOLEAN); field->u.boolean.value = value; + + purple_request_fields_check_others_sensitivity(field); } gboolean @@ -1389,6 +1471,8 @@ g_return_if_fail(field->type == PURPLE_REQUEST_FIELD_CHOICE); field->u.choice.value = value; + + purple_request_fields_check_others_sensitivity(field); } gpointer @@ -1741,6 +1825,8 @@ g_return_if_fail(field->type == PURPLE_REQUEST_FIELD_ACCOUNT); field->u.account.account = value; + + purple_request_fields_check_others_sensitivity(field); } void @@ -2231,6 +2317,8 @@ purple_request_fields_strip_html(fields); } + purple_request_fields_check_sensitivity(fields); + if (ops != NULL && ops->request_fields != NULL) { PurpleRequestInfo *info; gchar **tmp;
--- a/libpurple/request.h Thu Oct 24 20:24:29 2013 +0530 +++ b/libpurple/request.h Fri Oct 25 00:31:39 2013 +0530 @@ -179,6 +179,8 @@ typedef gboolean (*PurpleRequestFieldValidator)(PurpleRequestField *field, gchar **errmsg, gpointer user_data); +typedef gboolean (*PurpleRequestFieldSensitivityCb)(PurpleRequestField *field); + /** The type of callbacks passed to purple_request_action(). The first * argument is the @a user_data parameter; the second is the index in the list * of actions of the one chosen. @@ -516,6 +518,16 @@ const PurpleRequestFields *fields); /** + * Returns a list of all fields with sensitivity callback added. + * + * @param fields The fields list. + * + * @constreturn The list of fields with automatic sensitivity callback. + */ +const GList * +purple_request_fields_get_autosensitive(const PurpleRequestFields *fields); + +/** * Returns whether or not a field with the specified ID is required. * * @param fields The fields list. @@ -886,6 +898,33 @@ gboolean purple_request_field_is_valid(PurpleRequestField *field, gchar **errmsg); /** + * Sets field editable. + * + * @param field The field. + * @param sensitive TRUE if the field should be sensitive for user input. + */ +void purple_request_field_set_sensitive(PurpleRequestField *field, + gboolean sensitive); + +/** + * Checks, if field is editable. + * + * @param field The field. + * + * @return TRUE, if the field is sensitive for user input. + */ +gboolean purple_request_field_is_sensitive(PurpleRequestField *field); + +/** + * Sets the callback, used to determine if the field should be editable. + * + * @param field The field. + * @param cb The callback. + */ +void purple_request_field_set_sensitivity_cb(PurpleRequestField *field, + PurpleRequestFieldSensitivityCb cb); + +/** * Returns the ui_data for a field. * * @param field The field. @@ -956,15 +995,6 @@ gboolean masked); /** - * Sets whether or not a string field is editable. - * - * @param field The field. - * @param editable The editable value. - */ -void purple_request_field_string_set_editable(PurpleRequestField *field, - gboolean editable); - -/** * Returns the default value in a string field. * * @param field The field. @@ -1001,15 +1031,6 @@ */ gboolean purple_request_field_string_is_masked(const PurpleRequestField *field); -/** - * Returns whether or not a string field is editable. - * - * @param field The field. - * - * @return @c TRUE if the field is editable, or @c FALSE otherwise. - */ -gboolean purple_request_field_string_is_editable(const PurpleRequestField *field); - /*@}*/ /**************************************************************************/
--- a/libpurple/util.c Thu Oct 24 20:24:29 2013 +0530 +++ b/libpurple/util.c Fri Oct 25 00:31:39 2013 +0530 @@ -40,6 +40,7 @@ PurpleCallback callback; gpointer data; GList *children; + gchar *stock_icon; }; static char *custom_user_dir = NULL; @@ -65,6 +66,7 @@ { g_return_if_fail(act != NULL); + g_free(act->stock_icon); g_free(act->label); g_free(act); } @@ -125,6 +127,21 @@ act->children = children; } +void purple_menu_action_set_stock_icon(PurpleMenuAction *act, + const gchar *stock) +{ + g_return_if_fail(act != NULL); + + g_free(act->stock_icon); + act->stock_icon = g_strdup(stock); +} + +const gchar * +purple_menu_action_get_stock_icon(PurpleMenuAction *act) +{ + return act->stock_icon; +} + void purple_util_init(void) {
--- a/libpurple/util.h Thu Oct 24 20:24:29 2013 +0530 +++ b/libpurple/util.h Fri Oct 25 00:31:39 2013 +0530 @@ -154,6 +154,25 @@ void purple_menu_action_set_children(PurpleMenuAction *act, GList *children); /** + * Sets the icon for the PurpleMenuAction. + * + * @param act The menu action. + * @param strock The stock icon identifier. + */ +void purple_menu_action_set_stock_icon(PurpleMenuAction *act, + const gchar *stock); + +/** + * Gets the stock icon of the PurpleMenuAction. + * + * @param act The menu action. + * + * @return The stock icon identifier. + */ +const gchar * +purple_menu_action_get_stock_icon(PurpleMenuAction *act); + +/** * Set the appropriate presence values for the currently playing song. * * @param title The title of the song, @c NULL to unset the value.
--- a/pidgin/gtkconv.c Thu Oct 24 20:24:29 2013 +0530 +++ b/pidgin/gtkconv.c Fri Oct 25 00:31:39 2013 +0530 @@ -50,6 +50,7 @@ #include "util.h" #include "version.h" +#include "gtkinternal.h" #include "gtkdnd-hints.h" #include "gtkblist.h" #include "gtkconv.h" @@ -119,7 +120,8 @@ PIDGIN_CONV_TAB_ICON = 1 << 3, PIDGIN_CONV_TOPIC = 1 << 4, PIDGIN_CONV_SMILEY_THEME = 1 << 5, - PIDGIN_CONV_COLORIZE_TITLE = 1 << 6 + PIDGIN_CONV_COLORIZE_TITLE = 1 << 6, + PIDGIN_CONV_E2EE = 1 << 7 }PidginConvFields; enum { @@ -187,6 +189,7 @@ static GList *xa_list = NULL; static GList *offline_list = NULL; static GHashTable *prpl_lists = NULL; +static GHashTable *e2ee_stock = NULL; static PurpleTheme *default_conv_theme = NULL; @@ -3916,12 +3919,28 @@ return FALSE; } -static void -create_sendto_item(GtkWidget *menu, GtkSizeGroup *sg, GSList **group, PurpleBuddy *buddy, PurpleAccount *account, const char *name) +static GtkWidget * +e2ee_state_to_gtkimage(PurpleE2eeState *state) +{ + PurpleStoredImage *img; + + img = _pidgin_e2ee_stock_icon_get( + purple_e2ee_state_get_stock_icon(state)); + if (!img) + return NULL; + + return gtk_image_new_from_pixbuf(pidgin_pixbuf_from_imgstore(img)); +} + +static void +create_sendto_item(GtkWidget *menu, GtkSizeGroup *sg, GSList **group, + PurpleBuddy *buddy, PurpleAccount *account, const char *name, + gboolean e2ee_enabled) { GtkWidget *box; GtkWidget *label; GtkWidget *image; + GtkWidget *e2ee_image = NULL; GtkWidget *menuitem; GdkPixbuf *pixbuf; gchar *text; @@ -3938,6 +3957,20 @@ g_object_unref(G_OBJECT(pixbuf)); } + if (e2ee_enabled) { + PurpleIMConversation *im; + PurpleE2eeState *state = NULL; + + im = purple_conversations_find_im_with_account( + purple_buddy_get_name(buddy), purple_buddy_get_account(buddy)); + if (im) + state = purple_conversation_get_e2ee_state(PURPLE_CONVERSATION(im)); + if (state) + e2ee_image = e2ee_state_to_gtkimage(state); + else + e2ee_image = gtk_image_new(); + } + gtk_size_group_add_widget(sg, image); /* Make our menu item */ @@ -3954,7 +3987,10 @@ gtk_container_remove(GTK_CONTAINER(menuitem), label); gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 4); + if (e2ee_image) + gtk_box_pack_start(GTK_BOX(box), e2ee_image, FALSE, FALSE, 0); if (buddy != NULL && !purple_presence_is_online(purple_buddy_get_presence(buddy))) @@ -3977,6 +4013,8 @@ gtk_widget_show(label); gtk_widget_show(image); + if (e2ee_image) + gtk_widget_show(e2ee_image); gtk_widget_show(box); /* Set our data and callbacks. */ @@ -4042,6 +4080,7 @@ } else { + gboolean e2ee_enabled = FALSE; GList *list = NULL, *iter; for (l = buds; l != NULL; l = l->next) { @@ -4053,10 +4092,15 @@ { PurpleBuddy *buddy = (PurpleBuddy *)node; PurpleAccount *account; + PurpleIMConversation *im; if (!PURPLE_IS_BUDDY(node)) continue; + im = purple_conversations_find_im_with_account(purple_buddy_get_name(buddy), purple_buddy_get_account(buddy)); + if (im && purple_conversation_get_e2ee_state(PURPLE_CONVERSATION(im)) != NULL) + e2ee_enabled = TRUE; + account = purple_buddy_get_account(buddy); /* TODO WEBKIT: (I'm not actually sure if this is webkit-related --Mark Doliner) */ if (purple_account_is_connected(account) /*|| account == purple_conversation_get_account(gtkconv->active_conv)*/) @@ -4077,7 +4121,7 @@ PurplePresence *pre = iter->data; PurpleBuddy *buddy = purple_buddy_presence_get_buddy(PURPLE_BUDDY_PRESENCE(pre)); create_sendto_item(menu, sg, &group, buddy, - purple_buddy_get_account(buddy), purple_buddy_get_name(buddy)); + purple_buddy_get_account(buddy), purple_buddy_get_name(buddy), e2ee_enabled); } } g_list_free(list); @@ -4094,6 +4138,92 @@ update_send_to_selection(win); } +PurpleStoredImage * +_pidgin_e2ee_stock_icon_get(const gchar *stock_name) +{ + gchar filename[100], *path; + PurpleStoredImage *image; + + /* core is quitting */ + if (e2ee_stock == NULL) + return NULL; + + if (g_hash_table_lookup_extended(e2ee_stock, stock_name, NULL, (gpointer*)&image)) + return image; + + g_snprintf(filename, sizeof(filename), "%s.png", stock_name); + path = g_build_filename(DATADIR, "pixmaps", "pidgin", "e2ee", "16", + filename, NULL); + image = purple_imgstore_new_from_file(path); + g_free(path); + + g_hash_table_insert(e2ee_stock, g_strdup(stock_name), image); + return image; +} + +static void +generate_e2ee_controls(PidginWindow *win) +{ + PidginConversation *gtkconv; + PurpleConversation *conv; + PurpleE2eeState *state; + PurpleE2eeProvider *provider; + GtkWidget *menu; + PurpleE2eeConvMenuCallback menu_cb; + GList *menu_actions = NULL, *it; + GtkWidget *e2ee_image; + + gtkconv = pidgin_conv_window_get_active_gtkconv(win); + g_return_if_fail(gtkconv != NULL); + + conv = gtkconv->active_conv; + g_return_if_fail(conv != NULL); + + if (win->menu->e2ee != NULL) { + gtk_widget_destroy(win->menu->e2ee); + win->menu->e2ee = NULL; + } + + provider = purple_e2ee_provider_get_main(); + state = purple_conversation_get_e2ee_state(conv); + if (state == NULL || provider == NULL) + return; + if (purple_e2ee_state_get_provider(state) != provider) + return; + + win->menu->e2ee = gtk_image_menu_item_new_with_label( + purple_e2ee_provider_get_name(provider)); + + menu = gtk_menu_new(); + gtk_menu_shell_insert(GTK_MENU_SHELL(win->menu->menubar), + win->menu->e2ee, 3); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(win->menu->e2ee), menu); + + e2ee_image = e2ee_state_to_gtkimage(state); + if (e2ee_image) { + gtk_image_menu_item_set_image( + GTK_IMAGE_MENU_ITEM(win->menu->e2ee), e2ee_image); + } + + gtk_widget_set_tooltip_text(win->menu->e2ee, + purple_e2ee_state_get_name(state)); + + menu_cb = purple_e2ee_provider_get_conv_menu_cb(provider); + if (menu_cb) + menu_actions = menu_cb(conv); + + for (it = g_list_first(menu_actions); it; it = g_list_next(it)) { + PurpleMenuAction *action = it->data; + + gtk_widget_show_all( + pidgin_append_menu_action(menu, action, conv)); + } + g_list_free(menu_actions); + + gtk_widget_show(win->menu->e2ee); + gtk_widget_show(menu); +} + static const char * get_chat_user_status_icon(PurpleChatConversation *chat, const char *name, PurpleChatUserFlags flags) { @@ -7364,6 +7494,9 @@ regenerate_plugins_items(win); } + if (fields & PIDGIN_CONV_E2EE) + generate_e2ee_controls(win); + if (fields & PIDGIN_CONV_TAB_ICON) { update_tab_icon(conv); @@ -7560,6 +7693,10 @@ { flags = PIDGIN_CONV_MENU; } + else if (type == PURPLE_CONVERSATION_UPDATE_E2EE) + { + flags = PIDGIN_CONV_E2EE | PIDGIN_CONV_MENU; + } pidgin_conv_update_fields(conv, flags); } @@ -8299,8 +8436,9 @@ static void update_conversation_switched(PurpleConversation *conv) { - pidgin_conv_update_fields(conv, PIDGIN_CONV_TAB_ICON | PIDGIN_CONV_SET_TITLE | - PIDGIN_CONV_MENU | PIDGIN_CONV_BUDDY_ICON); + pidgin_conv_update_fields(conv, PIDGIN_CONV_TAB_ICON | + PIDGIN_CONV_SET_TITLE | PIDGIN_CONV_MENU | + PIDGIN_CONV_BUDDY_ICON | PIDGIN_CONV_E2EE ); } static void @@ -8505,6 +8643,17 @@ return &handle; } +static void +e2ee_stock_delete_value(gpointer value) +{ + PurpleStoredImage *img = value; + + purple_imgstore_unref(img); +} + +static void +pidgin_conversations_pre_uninit(void); + void pidgin_conversations_init(void) { @@ -8512,6 +8661,9 @@ void *blist_handle = purple_blist_get_handle(); char *theme_dir; + e2ee_stock = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + e2ee_stock_delete_value); + /* Conversations */ purple_prefs_add_none(PIDGIN_PREFS_ROOT "/conversations"); purple_prefs_add_none(PIDGIN_PREFS_ROOT "/conversations/themes"); @@ -8602,8 +8754,6 @@ purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/conversations/im/hide_new", hide_new_pref_cb, NULL); - - /********************************************************************** * Register signals **********************************************************************/ @@ -8725,6 +8875,9 @@ purple_signal_connect(purple_accounts_get_handle(), "account-status-changed", handle, PURPLE_CALLBACK(account_status_changed_cb), NULL); + purple_signal_connect_priority(purple_get_core(), "quitting", handle, + PURPLE_CALLBACK(pidgin_conversations_pre_uninit), NULL, PURPLE_SIGNAL_PRIORITY_HIGHEST); + /* Callbacks to update a conversation */ purple_signal_connect(blist_handle, "blist-node-added", handle, G_CALLBACK(buddy_update_cb), NULL); @@ -8810,6 +8963,13 @@ #endif } +static void +pidgin_conversations_pre_uninit(void) +{ + g_hash_table_destroy(e2ee_stock); + e2ee_stock = NULL; +} + void pidgin_conversations_uninit(void) { @@ -9814,6 +9974,7 @@ purple_conversation_is_logging(conv)); generate_send_to_items(win); + generate_e2ee_controls(win); regenerate_options_items(win); regenerate_plugins_items(win);
--- a/pidgin/gtkconvwin.h Thu Oct 24 20:24:29 2013 +0530 +++ b/pidgin/gtkconvwin.h Fri Oct 25 00:31:39 2013 +0530 @@ -66,6 +66,7 @@ GtkAction *show_formatting_toolbar; GtkWidget *send_to; + GtkWidget *e2ee; GtkWidget *tray;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkinternal.h Fri Oct 25 00:31:39 2013 +0530 @@ -0,0 +1,36 @@ +/** + * @file gtkinternal.h Internal definitions and includes for Pidgin + * @ingroup pidgin + */ + +/* pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ +#ifndef _PIDGIN_INTERNAL_H_ +#define _PIDGIN_INTERNAL_H_ + +G_BEGIN_DECLS + +PurpleStoredImage * +_pidgin_e2ee_stock_icon_get(const gchar *stock_name); + +G_END_DECLS + +#endif /* _PIDGIN_INTERNAL_H_ */
--- a/pidgin/gtkrequest.c Thu Oct 24 20:24:29 2013 +0530 +++ b/pidgin/gtkrequest.c Fri Oct 25 00:31:39 2013 +0530 @@ -1073,6 +1073,7 @@ PurpleRequestFieldGroup *group; PurpleRequestFields *fields; PidginRequestData *req_data; + const GList *it; group = purple_request_field_get_group(field); fields = purple_request_field_group_get_fields_list(group); @@ -1081,6 +1082,20 @@ gtk_widget_set_sensitive(req_data->ok_button, purple_request_fields_all_required_filled(fields) && purple_request_fields_all_valid(fields)); + + it = purple_request_fields_get_autosensitive(fields); + for (; it != NULL; it = g_list_next(it)) { + PurpleRequestField *field = it->data; + GtkWidget *widget = purple_request_field_get_ui_data(field); + gboolean sensitive; + + sensitive = purple_request_field_is_sensitive(field); + gtk_widget_set_sensitive(widget, sensitive); + + /* XXX: and what about multiline? */ + if (GTK_IS_EDITABLE(widget)) + gtk_editable_set_editable(GTK_EDITABLE(widget), sensitive); + } } static void @@ -1138,7 +1153,7 @@ gboolean is_editable; value = purple_request_field_string_get_default_value(field); - is_editable = purple_request_field_string_is_editable(field); + is_editable = purple_request_field_is_sensitive(field); if (purple_request_field_string_is_multiline(field)) { @@ -1167,7 +1182,6 @@ gtk_widget_set_tooltip_text(textview, purple_request_field_get_tooltip(field)); gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), is_editable); - gtk_widget_set_sensitive(textview, is_editable); g_signal_connect(G_OBJECT(textview), "focus-out-event", G_CALLBACK(field_string_focus_out_cb), field); @@ -1198,7 +1212,6 @@ } gtk_editable_set_editable(GTK_EDITABLE(widget), is_editable); - gtk_widget_set_sensitive(widget, is_editable); g_signal_connect(G_OBJECT(widget), "focus-out-event", G_CALLBACK(field_string_focus_out_cb), field); @@ -1876,6 +1889,9 @@ continue; } + gtk_widget_set_sensitive(widget, + purple_request_field_is_sensitive(field)); + if (label) gtk_label_set_mnemonic_widget(GTK_LABEL(label), widget);
--- a/pidgin/gtkutils.c Thu Oct 24 20:24:29 2013 +0530 +++ b/pidgin/gtkutils.c Fri Oct 25 00:31:39 2013 +0530 @@ -1724,13 +1724,31 @@ { GtkWidget *menuitem; GList *list; + const gchar *stock_id; + GtkWidget *icon_image = NULL; if (act == NULL) { return pidgin_separator(menu); } + stock_id = purple_menu_action_get_stock_icon(act); + if (stock_id) { + icon_image = gtk_image_new_from_stock(stock_id, + gtk_icon_size_from_name( + PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL)); + } + + if (icon_image) { + menuitem = gtk_image_menu_item_new_with_mnemonic( + purple_menu_action_get_label(act)); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), + icon_image); + } else { + menuitem = gtk_menu_item_new_with_mnemonic( + purple_menu_action_get_label(act)); + } + list = purple_menu_action_get_children(act); - menuitem = gtk_menu_item_new_with_mnemonic(purple_menu_action_get_label(act)); if (list == NULL) { PurpleCallback callback;
--- a/pidgin/gtkwebview.c Thu Oct 24 20:24:29 2013 +0530 +++ b/pidgin/gtkwebview.c Fri Oct 25 00:31:39 2013 +0530 @@ -34,6 +34,7 @@ #include "gtkwebview.h" #include "gtkwebviewtoolbar.h" +#include "gtkinternal.h" #include "gtk3compat.h" #define MAX_FONT_SIZE 7 @@ -687,12 +688,12 @@ gpointer user_data) { const gchar *uri; + PurpleStoredImage *img = NULL; + const char *filename; uri = webkit_network_request_get_uri(request); if (purple_str_has_prefix(uri, PURPLE_STORED_IMAGE_PROTOCOL)) { int id; - PurpleStoredImage *img; - const char *filename; uri += sizeof(PURPLE_STORED_IMAGE_PROTOCOL) - 1; id = strtoul(uri, NULL, 10); @@ -700,17 +701,52 @@ img = purple_imgstore_find_by_id(id); if (!img) return; - + } else if (purple_str_has_prefix(uri, PURPLE_STOCK_IMAGE_PROTOCOL)) { + gchar *p_uri, *found; + const gchar *domain, *stock_name; + + uri += sizeof(PURPLE_STOCK_IMAGE_PROTOCOL) - 1; + + p_uri = g_strdup(uri); + found = strchr(p_uri, '/'); + if (!found) { + purple_debug_warning("webview", "Invalid purple stock " + "image uri: %s", uri); + return; + } + + found[0] = '\0'; + domain = p_uri; + stock_name = found + 1; + + if (g_strcmp0(domain, "e2ee") == 0) { + img = _pidgin_e2ee_stock_icon_get(stock_name); + if (!img) + return; + } else { + purple_debug_warning("webview", "Invalid purple stock " + "image domain: %s", domain); + return; + } + } else + return; + + if (img != NULL) { filename = purple_imgstore_get_filename(img); if (filename && g_path_is_absolute(filename)) { - char *tmp = g_strdup_printf("file://%s", filename); + gchar *tmp = g_strdup_printf("file://%s", filename); webkit_network_request_set_uri(request, tmp); g_free(tmp); } else { - char *b64 = purple_base64_encode(purple_imgstore_get_data(img), - purple_imgstore_get_size(img)); - const char *type = purple_imgstore_get_extension(img); - char *tmp = g_strdup_printf("data:image/%s;base64,%s", type, b64); + gchar *b64, *tmp; + const gchar *type; + + b64 = purple_base64_encode( + purple_imgstore_get_data(img), + purple_imgstore_get_size(img)); + type = purple_imgstore_get_extension(img); + tmp = g_strdup_printf("data:image/%s;base64,%s", + type, b64); webkit_network_request_set_uri(request, tmp); g_free(b64); g_free(tmp);
--- a/pidgin/pixmaps/Makefile.am Thu Oct 24 20:24:29 2013 +0530 +++ b/pidgin/pixmaps/Makefile.am Fri Oct 25 00:31:39 2013 +0530 @@ -88,6 +88,12 @@ dialogs/scalable/question.svg \ dialogs/scalable/warning.svg +E2EE_STATES_16 = \ + e2ee/16/finished.png \ + e2ee/16/not-private.png \ + e2ee/16/private.png \ + e2ee/16/unverified.png + EMBLEMS_16 = \ emblems/16/aol-client.png \ emblems/16/birthday.png \ @@ -561,6 +567,7 @@ $(DIALOGS_16) \ $(DIALOGS_64) \ $(DIALOGS_SCALABLE) \ + $(E2EE_STATES_16) \ $(EMBLEMS_16) \ $(EMBLEMS_SCALABLE) \ $(PROTOCOLS_16) \