/* * text-helper.c - Source for TextHelper * Copyright (C) 2006,2010 Collabora Ltd. * Copyright (C) 2006 Nokia Corporation * @author Ole Andre Vadla Ravnaas * @author Robert McQueen * @author Senko Rasic * @author Sjoerd Simons * * 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.1 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 St, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE /* Needed for strptime (_XOPEN_SOURCE can also be used). */ #include #include #include #include #include #include #include #include #define DEBUG_FLAG DEBUG_IM #include "debug.h" #include "text-helper.h" #include "util.h" static void add_text (WockyStanza *stanza, const gchar *text) { WockyNode *node = wocky_stanza_get_top_node (stanza); WockyNode *htmlnode; wocky_node_add_child_with_content (node, "body", text); /* Add plain xhtml-im node */ htmlnode = wocky_node_add_child_ns (node, "html", WOCKY_XMPP_NS_XHTML_IM); wocky_node_add_child_with_content_ns (htmlnode, "body", text, WOCKY_W3C_NS_XHTML); } static WockyStanza * create_message_stanza (const gchar *from, const gchar *to, SalutContact *contact, TpChannelTextMessageType type, const gchar *text, GError **error) { WockyStanza *stanza; WockyNode *node; if (type > TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE) { DEBUG ("invalid message type %u", type); g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "invalid message type: %u", type); return NULL; } stanza = wocky_stanza_new ("message", WOCKY_XMPP_NS_JABBER_CLIENT); node = wocky_stanza_get_top_node (stanza); wocky_node_set_attribute (node, "from", from); wocky_node_set_attribute (node, "to", to); if (type == TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION) { gchar *tmp; tmp = g_strconcat ("/me ", text, NULL); add_text (stanza, tmp); g_free (tmp); } else { add_text (stanza, text); } if (contact != NULL) wocky_stanza_set_to_contact (stanza, WOCKY_CONTACT (contact)); return stanza; } WockyStanza * text_helper_create_message (const gchar *from, SalutContact *to, TpChannelTextMessageType type, const gchar *text, GError **error) { WockyStanza *stanza; WockyNode *node; stanza = create_message_stanza (from, to->name, to, type, text, error); node = wocky_stanza_get_top_node (stanza); if (stanza == NULL) return NULL; switch (type) { case TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL: case TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION: wocky_node_set_attribute (node, "type", "chat"); break; case TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE: wocky_node_set_attribute (node, "type", "normal"); break; default: g_assert_not_reached (); break; } return stanza; } WockyStanza * text_helper_create_message_groupchat (const gchar *from, const gchar *to, TpChannelTextMessageType type, const gchar *text, GError **error) { WockyStanza *stanza; WockyNode *node; stanza = create_message_stanza (from, to, NULL, type, text, error); if (stanza == NULL) return NULL; node = wocky_stanza_get_top_node (stanza); switch (type) { case TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL: case TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION: wocky_node_set_attribute (node, "type", "groupchat"); break; case TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE: wocky_node_set_attribute (node, "type", "normal"); break; default: g_assert_not_reached (); break; } return stanza; } gboolean text_helper_parse_incoming_message (WockyStanza *stanza, const gchar **from, TpChannelTextMessageType *msgtype, const gchar **body, const gchar **body_offset) { const gchar *type; WockyNode *node; WockyNode *event; WockyNode *top_node = wocky_stanza_get_top_node (stanza); *from = wocky_node_get_attribute (top_node, "from"); type = wocky_node_get_attribute (top_node, "type"); /* Work around iChats strange way of doing typing notification */ event = wocky_node_get_child_ns (top_node, "x", WOCKY_XMPP_NS_EVENT); if (event != NULL) { /* If the event has a composing and an id child, this is a typing * notification and it should be dropped */ if (wocky_node_get_child (event, "composing") != NULL && wocky_node_get_child (event, "id") != NULL) { return FALSE; } } /* * Parse body if it exists. */ node = wocky_node_get_child (top_node, "body"); if (node) { *body = node->content; } else { *body = NULL; } /* Messages starting with /me are ACTION messages, and the /me should be * removed. type="chat" messages are NORMAL. everything else is * something that doesn't necessarily expect a reply or ongoing * conversation ("normal") or has been auto-sent, so we make it NOTICE in * all other cases. */ *msgtype = TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE; *body_offset = *body; if (*body) { if (0 == strncmp (*body, "/me ", 4)) { *msgtype = TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION; *body_offset = *body + 4; } else if (type != NULL && (0 == strcmp (type, "chat") || 0 == strcmp (type, "groupchat"))) { *msgtype = TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL; *body_offset = *body; } } return TRUE; } gboolean text_helper_validate_tp_message (TpMessage *message, guint *type, gchar **token, gchar **text, GError **error) { const GHashTable *part; guint msgtype = TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL; const gchar *msgtext; gchar *msgtoken; gboolean valid = TRUE; if (tp_message_count_parts (message) != 2) { g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "Invalid number of message parts, expected 2, got %d", tp_message_count_parts (message)); return FALSE; } part = tp_message_peek (message, 0); if (tp_asv_lookup (part, "message-type")) msgtype = tp_asv_get_uint32 (part, "message-type", &valid); if (!valid || msgtype > TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE) { g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "Invalid message type"); return FALSE; } part = tp_message_peek (message, 1); msgtext = tp_asv_get_string (part, "content"); if (msgtext == NULL) { g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "Empty message content"); return FALSE; } msgtoken = salut_generate_id (); tp_message_set_string (message, 0, "message-token", msgtoken); if (text != NULL) *text = g_strdup (msgtext); if (type != NULL) *type = msgtype; if (token != NULL) *token = msgtoken; else g_free (msgtoken); return TRUE; } void text_helper_report_delivery_error (TpSvcChannel *self, guint error_type, guint timestamp, guint type, const gchar *text, const gchar *token) { TpBaseConnection *base_conn; TpHandle handle; guint handle_type; TpMessage *message; TpMessage *delivery_echo; g_object_get (self, "connection", &base_conn, "handle", &handle, "handle-type", &handle_type, NULL); delivery_echo = tp_message_new (base_conn, 2, 2); tp_message_set_handle (delivery_echo, 0, "message-sender", TP_HANDLE_TYPE_CONTACT, base_conn->self_handle); tp_message_set_uint32 (delivery_echo, 0, "message-type", type); tp_message_set_int64 (delivery_echo, 0, "message-sent", (gint64)timestamp); tp_message_set_string (delivery_echo, 1, "content-type", "text/plain"); tp_message_set_string (delivery_echo, 1, "content", text); message = tp_message_new (base_conn, 1, 1); tp_message_set_handle (message, 0, "message-sender", handle_type, handle); tp_message_set_uint32 (message, 0, "message-type", TP_CHANNEL_TEXT_MESSAGE_TYPE_DELIVERY_REPORT); tp_message_set_uint32 (message, 0, "delivery-status", TP_DELIVERY_STATUS_TEMPORARILY_FAILED); tp_message_set_uint32 (message, 0, "delivery-error", TP_CHANNEL_TEXT_SEND_ERROR_OFFLINE); tp_message_set_string (message, 0, "delivery-token", token); tp_message_take_message (message, 0, "delivery-echo", delivery_echo); g_object_unref (base_conn); tp_message_mixin_take_received (G_OBJECT (self), message); } TpMessage * text_helper_create_received_message (TpBaseConnection *base_conn, guint sender_handle, guint timestamp, guint type, const gchar *text) { TpMessage *message = tp_message_new (base_conn, 2, 2); tp_message_set_uint32 (message, 0, "message-type", type); tp_message_set_handle (message, 0, "message-sender", TP_HANDLE_TYPE_CONTACT, sender_handle); tp_message_set_int64 (message, 0, "message-received", timestamp); tp_message_set_string (message, 1, "content-type", "text/plain"); tp_message_set_string (message, 1, "content", text); return message; }