diff options
Diffstat (limited to 'libempathy')
-rw-r--r-- | libempathy/empathy-contact.c | 21 | ||||
-rw-r--r-- | libempathy/empathy-contact.h | 3 | ||||
-rw-r--r-- | libempathy/empathy-request-util.c | 55 | ||||
-rw-r--r-- | libempathy/empathy-request-util.h | 5 | ||||
-rw-r--r-- | libempathy/empathy-tp-chat.c | 174 | ||||
-rw-r--r-- | libempathy/empathy-tp-chat.h | 8 | ||||
-rw-r--r-- | libempathy/empathy-utils.c | 75 | ||||
-rw-r--r-- | libempathy/empathy-utils.h | 2 |
8 files changed, 313 insertions, 30 deletions
diff --git a/libempathy/empathy-contact.c b/libempathy/empathy-contact.c index 680094a54..620c479e2 100644 --- a/libempathy/empathy-contact.c +++ b/libempathy/empathy-contact.c @@ -1098,6 +1098,18 @@ empathy_contact_get_status (EmpathyContact *contact) } gboolean +empathy_contact_can_sms (EmpathyContact *contact) +{ + EmpathyContactPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE); + + priv = GET_PRIV (contact); + + return priv->capabilities & EMPATHY_CAPABILITIES_SMS; +} + +gboolean empathy_contact_can_voip (EmpathyContact *contact) { EmpathyContactPriv *priv; @@ -1189,6 +1201,9 @@ empathy_contact_can_do_action (EmpathyContact *self, case EMPATHY_ACTION_CHAT: sensitivity = TRUE; break; + case EMPATHY_ACTION_SMS: + sensitivity = empathy_contact_can_sms (self); + break; case EMPATHY_ACTION_AUDIO_CALL: sensitivity = empathy_contact_can_voip_audio (self); break; @@ -1729,6 +1744,12 @@ tp_caps_to_capabilities (TpCapabilities *caps) TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_VIDEO, NULL)) capabilities |= EMPATHY_CAPABILITIES_VIDEO; } + else if (!tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_TEXT)) + { + if (tp_asv_get_boolean (fixed_prop, + TP_PROP_CHANNEL_INTERFACE_SMS_SMS_CHANNEL, NULL)) + capabilities |= EMPATHY_CAPABILITIES_SMS; + } } return capabilities; diff --git a/libempathy/empathy-contact.h b/libempathy/empathy-contact.h index f9217c108..16b50e500 100644 --- a/libempathy/empathy-contact.h +++ b/libempathy/empathy-contact.h @@ -67,6 +67,7 @@ typedef enum { EMPATHY_CAPABILITIES_VIDEO = 1 << 1, EMPATHY_CAPABILITIES_FT = 1 << 2, EMPATHY_CAPABILITIES_RFB_STREAM_TUBE = 1 << 3, + EMPATHY_CAPABILITIES_SMS = 1 << 4, EMPATHY_CAPABILITIES_UNKNOWN = 1 << 7 } EmpathyCapabilities; @@ -94,6 +95,7 @@ void empathy_contact_set_is_user (EmpathyContact *contact, gboolean is_user); gboolean empathy_contact_is_online (EmpathyContact *contact); const gchar * empathy_contact_get_status (EmpathyContact *contact); +gboolean empathy_contact_can_sms (EmpathyContact *contact); gboolean empathy_contact_can_voip (EmpathyContact *contact); gboolean empathy_contact_can_voip_audio (EmpathyContact *contact); gboolean empathy_contact_can_voip_video (EmpathyContact *contact); @@ -102,6 +104,7 @@ gboolean empathy_contact_can_use_rfb_stream_tube (EmpathyContact *contact); typedef enum { EMPATHY_ACTION_CHAT, + EMPATHY_ACTION_SMS, EMPATHY_ACTION_AUDIO_CALL, EMPATHY_ACTION_VIDEO_CALL, EMPATHY_ACTION_VIEW_LOGS, diff --git a/libempathy/empathy-request-util.c b/libempathy/empathy-request-util.c index ea885dacc..6472230ab 100644 --- a/libempathy/empathy-request-util.c +++ b/libempathy/empathy-request-util.c @@ -58,9 +58,11 @@ ensure_text_channel_cb (GObject *source, } } -void -empathy_chat_with_contact_id (TpAccount *account, - const gchar *contact_id, +static void +create_text_channel (TpAccount *account, + TpHandleType target_handle_type, + const gchar *target_id, + gboolean sms_channel, gint64 timestamp) { GHashTable *request; @@ -69,39 +71,46 @@ empathy_chat_with_contact_id (TpAccount *account, request = tp_asv_new ( TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_TEXT, - TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT, - TP_PROP_CHANNEL_TARGET_ID, G_TYPE_STRING, contact_id, + TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, target_handle_type, + TP_PROP_CHANNEL_TARGET_ID, G_TYPE_STRING, target_id, NULL); + if (sms_channel) + tp_asv_set_boolean (request, + TP_PROP_CHANNEL_INTERFACE_SMS_SMS_CHANNEL, TRUE); + req = tp_account_channel_request_new (account, request, timestamp); - tp_account_channel_request_ensure_channel_async (req, EMPATHY_CHAT_BUS_NAME, - NULL, ensure_text_channel_cb, NULL); + tp_account_channel_request_ensure_channel_async (req, NULL, NULL, + ensure_text_channel_cb, NULL); g_hash_table_unref (request); g_object_unref (req); } void +empathy_chat_with_contact_id (TpAccount *account, + const gchar *contact_id, + gint64 timestamp) +{ + create_text_channel (account, TP_HANDLE_TYPE_CONTACT, + contact_id, FALSE, timestamp); +} + +void empathy_join_muc (TpAccount *account, const gchar *room_name, gint64 timestamp) { - GHashTable *request; - TpAccountChannelRequest *req; - - request = tp_asv_new ( - TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, - TP_IFACE_CHANNEL_TYPE_TEXT, - TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_ROOM, - TP_PROP_CHANNEL_TARGET_ID, G_TYPE_STRING, room_name, - NULL); - - req = tp_account_channel_request_new (account, request, timestamp); - - tp_account_channel_request_ensure_channel_async (req, EMPATHY_CHAT_BUS_NAME, - NULL, ensure_text_channel_cb, NULL); + create_text_channel (account, TP_HANDLE_TYPE_ROOM, + room_name, FALSE, timestamp); +} - g_hash_table_unref (request); - g_object_unref (req); +void +empathy_sms_contact_id (TpAccount *account, + const gchar *contact_id, + gint64 timestamp) +{ + create_text_channel (account, TP_HANDLE_TYPE_CONTACT, + contact_id, TRUE, timestamp); } diff --git a/libempathy/empathy-request-util.h b/libempathy/empathy-request-util.h index afb8013cb..49b98a433 100644 --- a/libempathy/empathy-request-util.h +++ b/libempathy/empathy-request-util.h @@ -52,6 +52,11 @@ void empathy_join_muc (TpAccount *account, const gchar *roomname, gint64 timestamp); +/* Request a sms channel */ +void empathy_sms_contact_id (TpAccount *account, + const gchar *contact_id, + gint64 timestamp); + G_END_DECLS #endif /* __EMPATHY_DISPATCHER_H__ */ diff --git a/libempathy/empathy-tp-chat.c b/libempathy/empathy-tp-chat.c index a56c08a32..72c6bebf0 100644 --- a/libempathy/empathy-tp-chat.c +++ b/libempathy/empathy-tp-chat.c @@ -59,6 +59,10 @@ typedef struct { gboolean got_password_flags; gboolean ready; gboolean can_upgrade_to_muc; + gboolean got_sms_channel; + gboolean sms_channel; + + GHashTable *messages_being_sent; } EmpathyTpChatPriv; static void tp_chat_iface_init (EmpathyContactListIface *iface); @@ -70,6 +74,8 @@ enum { PROP_REMOTE_CONTACT, PROP_PASSWORD_NEEDED, PROP_READY, + PROP_SMS_CHANNEL, + PROP_N_MESSAGES_SENDING, }; enum { @@ -90,6 +96,40 @@ G_DEFINE_TYPE_WITH_CODE (EmpathyTpChat, empathy_tp_chat, G_TYPE_OBJECT, static void acknowledge_messages (EmpathyTpChat *chat, GArray *ids); static void +tp_chat_set_delivery_status (EmpathyTpChat *self, + const gchar *token, + EmpathyDeliveryStatus delivery_status) +{ + EmpathyTpChatPriv *priv = GET_PRIV (self); + TpDeliveryReportingSupportFlags flags = + tp_text_channel_get_delivery_reporting_support ( + TP_TEXT_CHANNEL (priv->channel)); + + /* channel must support receiving failures and successes */ + if (!tp_str_empty (token) && + flags & TP_DELIVERY_REPORTING_SUPPORT_FLAG_RECEIVE_FAILURES && + flags & TP_DELIVERY_REPORTING_SUPPORT_FLAG_RECEIVE_SUCCESSES) { + + DEBUG ("Delivery status (%s) = %u", token, delivery_status); + + switch (delivery_status) { + case EMPATHY_DELIVERY_STATUS_NONE: + g_hash_table_remove (priv->messages_being_sent, + token); + break; + + default: + g_hash_table_insert (priv->messages_being_sent, + g_strdup (token), + GUINT_TO_POINTER (delivery_status)); + break; + } + + g_object_notify (G_OBJECT (self), "n-messages-sending"); + } +} + +static void tp_chat_invalidated_cb (TpProxy *proxy, guint domain, gint code, @@ -319,19 +359,38 @@ handle_delivery_report (EmpathyTpChat *self, gboolean valid; GPtrArray *echo; const gchar *message_body = NULL; + const gchar *delivery_dbus_error; + const gchar *delivery_token = NULL; header = tp_message_peek (message, 0); if (header == NULL) goto out; + delivery_token = tp_asv_get_string (header, "delivery-token"); delivery_status = tp_asv_get_uint32 (header, "delivery-status", &valid); - if (!valid || delivery_status != TP_DELIVERY_STATUS_PERMANENTLY_FAILED) + + if (!valid) { + goto out; + } else if (delivery_status == TP_DELIVERY_STATUS_ACCEPTED) { + DEBUG ("Accepted %s", delivery_token); + tp_chat_set_delivery_status (self, delivery_token, + EMPATHY_DELIVERY_STATUS_ACCEPTED); goto out; + } else if (delivery_status == TP_DELIVERY_STATUS_DELIVERED) { + DEBUG ("Delivered %s", delivery_token); + tp_chat_set_delivery_status (self, delivery_token, + EMPATHY_DELIVERY_STATUS_NONE); + goto out; + } else if (delivery_status != TP_DELIVERY_STATUS_PERMANENTLY_FAILED) { + goto out; + } delivery_error = tp_asv_get_uint32 (header, "delivery-error", &valid); if (!valid) delivery_error = TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN; + delivery_dbus_error = tp_asv_get_string (header, "delivery-dbus-error"); + /* TODO: ideally we should use tp-glib API giving us the echoed message as a * TpMessage. (fdo #35884) */ echo = tp_asv_get_boxed (header, "delivery-echo", @@ -344,7 +403,10 @@ handle_delivery_report (EmpathyTpChat *self, message_body = tp_asv_get_string (echo_body, "content"); } - g_signal_emit (self, signals[SEND_ERROR], 0, message_body, delivery_error); + tp_chat_set_delivery_status (self, delivery_token, + EMPATHY_DELIVERY_STATUS_NONE); + g_signal_emit (self, signals[SEND_ERROR], 0, message_body, + delivery_error, delivery_dbus_error); out: tp_text_channel_ack_message_async (TP_TEXT_CHANNEL (priv->channel), @@ -436,9 +498,10 @@ message_send_cb (GObject *source, { EmpathyTpChat *chat = user_data; TpTextChannel *channel = (TpTextChannel *) source; + gchar *token = NULL; GError *error = NULL; - if (!tp_text_channel_send_message_finish (channel, result, NULL, &error)) { + if (!tp_text_channel_send_message_finish (channel, result, &token, &error)) { DEBUG ("Error: %s", error->message); /* FIXME: we should use the body of the message as first argument of the @@ -446,10 +509,14 @@ message_send_cb (GObject *source, * we'll have rebased EmpathyTpChat on top of TpTextChannel we'll be able * to use the user_data pointer to pass the message and fix this. */ g_signal_emit (chat, signals[SEND_ERROR], 0, - NULL, error_to_text_send_error (error)); + NULL, error_to_text_send_error (error), NULL); g_error_free (error); } + + tp_chat_set_delivery_status (chat, token, + EMPATHY_DELIVERY_STATUS_SENDING); + g_free (token); } typedef struct { @@ -809,6 +876,7 @@ tp_chat_finalize (GObject *object) g_queue_free (priv->messages_queue); g_queue_free (priv->pending_messages_queue); + g_hash_table_destroy (priv->messages_being_sent); G_OBJECT_CLASS (empathy_tp_chat_parent_class)->finalize (object); } @@ -827,6 +895,9 @@ check_almost_ready (EmpathyTpChat *chat) if (!priv->got_password_flags) return; + if (!priv->got_sms_channel) + return; + /* We need either the members (room) or the remote contact (private chat). * If the chat is protected by a password we can't get these information so * consider the chat as ready so it can be presented to the user. */ @@ -1218,6 +1289,41 @@ got_password_flags_cb (TpChannel *proxy, check_almost_ready (EMPATHY_TP_CHAT (self)); } +static void +sms_channel_changed_cb (TpChannel *channel, + gboolean sms_channel, + gpointer user_data, + GObject *chat) +{ + EmpathyTpChatPriv *priv = GET_PRIV (chat); + + priv->sms_channel = sms_channel; + + g_object_notify (G_OBJECT (chat), "sms-channel"); +} + +static void +get_sms_channel_cb (TpProxy *channel, + const GValue *value, + const GError *in_error, + gpointer user_data, + GObject *chat) +{ + EmpathyTpChatPriv *priv = GET_PRIV (chat); + + if (in_error != NULL) { + DEBUG ("Failed to get SMSChannel: %s", in_error->message); + return; + } + + g_return_if_fail (G_VALUE_HOLDS_BOOLEAN (value)); + + priv->sms_channel = g_value_get_boolean (value); + priv->got_sms_channel = TRUE; + + check_almost_ready (EMPATHY_TP_CHAT (chat)); +} + static GObject * tp_chat_constructor (GType type, guint n_props, @@ -1327,6 +1433,22 @@ tp_chat_constructor (GType type, priv->got_password_flags = TRUE; } + /* Check if the chat is for SMS */ + if (tp_proxy_has_interface_by_id (priv->channel, + TP_IFACE_QUARK_CHANNEL_INTERFACE_SMS)) { + tp_cli_channel_interface_sms_connect_to_sms_channel_changed ( + priv->channel, + sms_channel_changed_cb, chat, NULL, G_OBJECT (chat), + NULL); + + tp_cli_dbus_properties_call_get (priv->channel, -1, + TP_IFACE_CHANNEL_INTERFACE_SMS, "SMSChannel", + get_sms_channel_cb, chat, NULL, G_OBJECT (chat)); + } else { + /* if there's no SMS support, then we're not waiting for it */ + priv->got_sms_channel = TRUE; + } + return chat; } @@ -1355,6 +1477,13 @@ tp_chat_get_property (GObject *object, case PROP_PASSWORD_NEEDED: g_value_set_boolean (value, empathy_tp_chat_password_needed (self)); break; + case PROP_SMS_CHANNEL: + g_value_set_boolean (value, priv->sms_channel); + break; + case PROP_N_MESSAGES_SENDING: + g_value_set_uint (value, + g_hash_table_size (priv->messages_being_sent)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -1436,6 +1565,22 @@ empathy_tp_chat_class_init (EmpathyTpChatClass *klass) FALSE, G_PARAM_READABLE)); + g_object_class_install_property (object_class, + PROP_SMS_CHANNEL, + g_param_spec_boolean ("sms-channel", + "SMS Channel", + "TRUE if channel is for sending SMSes", + FALSE, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, + PROP_N_MESSAGES_SENDING, + g_param_spec_uint ("n-messages-sending", + "Num Messages Sending", + "The number of messages being sent", + 0, G_MAXUINT, 0, + G_PARAM_READABLE)); + /* Signals */ signals[MESSAGE_RECEIVED] = g_signal_new ("message-received", @@ -1453,9 +1598,9 @@ empathy_tp_chat_class_init (EmpathyTpChatClass *klass) G_SIGNAL_RUN_LAST, 0, NULL, NULL, - _empathy_marshal_VOID__STRING_UINT, + _empathy_marshal_VOID__STRING_UINT_STRING, G_TYPE_NONE, - 2, G_TYPE_STRING, G_TYPE_UINT); + 3, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_STRING); signals[CHAT_STATE_CHANGED] = g_signal_new ("chat-state-changed", @@ -1499,6 +1644,8 @@ empathy_tp_chat_init (EmpathyTpChat *chat) chat->priv = priv; priv->messages_queue = g_queue_new (); priv->pending_messages_queue = g_queue_new (); + priv->messages_being_sent = g_hash_table_new_full ( + g_str_hash, g_str_equal, g_free, NULL); } static void @@ -1607,7 +1754,8 @@ empathy_tp_chat_send (EmpathyTpChat *chat, DEBUG ("Sending message: %s", message_body); tp_text_channel_send_message_async (TP_TEXT_CHANNEL (priv->channel), - message, 0, message_send_cb, chat); + message, TP_MESSAGE_SENDING_FLAG_REPORT_DELIVERY, + message_send_cb, chat); g_free (message_body); } @@ -1909,3 +2057,15 @@ empathy_tp_chat_get_self_contact (EmpathyTpChat *self) return priv->user; } + +gboolean +empathy_tp_chat_is_sms_channel (EmpathyTpChat *self) +{ + EmpathyTpChatPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self), FALSE); + + priv = GET_PRIV (self); + + return priv->sms_channel; +} diff --git a/libempathy/empathy-tp-chat.h b/libempathy/empathy-tp-chat.h index 7c998a3fa..83c7fe7d1 100644 --- a/libempathy/empathy-tp-chat.h +++ b/libempathy/empathy-tp-chat.h @@ -59,6 +59,12 @@ typedef struct { GValue *value; } EmpathyTpChatProperty; +typedef enum { + EMPATHY_DELIVERY_STATUS_NONE, + EMPATHY_DELIVERY_STATUS_SENDING, + EMPATHY_DELIVERY_STATUS_ACCEPTED +} EmpathyDeliveryStatus; + GType empathy_tp_chat_get_type (void) G_GNUC_CONST; EmpathyTpChat *empathy_tp_chat_new (TpAccount *account, TpChannel *channel); @@ -112,6 +118,8 @@ TpChannelChatState EmpathyContact * empathy_tp_chat_get_self_contact (EmpathyTpChat *self); +gboolean empathy_tp_chat_is_sms_channel (EmpathyTpChat *chat); + G_END_DECLS #endif /* __EMPATHY_TP_CHAT_H__ */ diff --git a/libempathy/empathy-utils.c b/libempathy/empathy-utils.c index f8220ac31..8173f781d 100644 --- a/libempathy/empathy-utils.c +++ b/libempathy/empathy-utils.c @@ -29,6 +29,7 @@ #include "config.h" #include <string.h> +#include <math.h> #include <time.h> #include <sys/types.h> @@ -957,3 +958,77 @@ empathy_get_x509_certificate_hostname (gnutls_x509_crt_t cert) return NULL; } + +gchar * +empathy_format_currency (gint amount, + guint scale, + const gchar *currency) +{ +#define MINUS "\342\210\222" +#define EURO "\342\202\254" +#define YEN "\302\245" +#define POUND "\302\243" + + /* localised representations of currency */ + /* FIXME: check these, especially negatives and decimals */ + static const struct { + const char *currency; + const char *positive; + const char *negative; + const char *decimal; + } currencies[] = { + /* sym positive negative decimal */ + { "EUR", EURO "%s", MINUS EURO "%s", "." }, + { "USD", "$%s", MINUS "$%s", "." }, + { "JPY", YEN "%s" MINUS YEN "%s", "." }, + { "GBP", POUND "%s", MINUS POUND "%s", "." }, + { "PLN", "%s zl", MINUS "%s zl", "." }, + { "BRL", "R$%s", MINUS "R$%s", "." }, + { "SEK", "%s kr", MINUS "%s kr", "." }, + { "DKK", "kr %s", "kr " MINUS "%s", "." }, + { "HKD", "$%s", MINUS "$%s", "." }, + { "CHF", "%s Fr.", MINUS "%s Fr.", "." }, + { "NOK", "kr %s", "kr" MINUS "%s", "," }, + { "CAD", "$%s", MINUS "$%s", "." }, + { "TWD", "$%s", MINUS "$%s", "." }, + { "AUD", "$%s", MINUS "$%s", "." }, + }; + + const char *positive = "%s"; + const char *negative = MINUS "%s"; + const char *decimal = "."; + char *fmt_amount, *money; + guint i; + + /* get the localised currency format */ + for (i = 0; i < G_N_ELEMENTS (currencies); i++) { + if (!tp_strdiff (currency, currencies[i].currency)) { + positive = currencies[i].positive; + negative = currencies[i].negative; + decimal = currencies[i].decimal; + break; + } + } + + /* format the amount using the scale */ + if (scale == 0) { + /* no decimal point required */ + fmt_amount = g_strdup_printf ("%d", amount); + } else { + /* don't use floating point arithmatic, it's noisy; + * we take the absolute values, because we want the minus + * sign to appear before the $ */ + int divisor = pow (10, scale); + int dollars = abs (amount / divisor); + int cents = abs (amount % divisor); + + fmt_amount = g_strdup_printf ("%d%s%0*d", + dollars, decimal, scale, cents); + } + + money = g_strdup_printf (amount < 0 ? negative : positive, fmt_amount); + g_free (fmt_amount); + + return money; +} + diff --git a/libempathy/empathy-utils.h b/libempathy/empathy-utils.h index de879021e..ac44535b8 100644 --- a/libempathy/empathy-utils.h +++ b/libempathy/empathy-utils.h @@ -117,6 +117,8 @@ gboolean empathy_folks_persona_is_interesting (FolksPersona *persona); gchar * empathy_get_x509_certificate_hostname (gnutls_x509_crt_t cert); +gchar *empathy_format_currency (gint amount, guint scale, const gchar *currency); + /* Copied from wocky/wocky-utils.h */ #define empathy_implement_finish_void(source, tag) \ |