--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/jabber/message.c Wed Feb 09 09:58:22 2005 -0500
@@ -0,0 +1,551 @@
+ * gaim - Jabber Protocol Plugin + * Copyright (C) 2003, Nathan Walp <faceprint@faceprint.com> + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#define JABBER_TYPING_NOTIFY_INT 15 +void jabber_message_free(JabberMessage *jm) +static void handle_chat(JabberMessage *jm) + JabberID *jid = jabber_id_new(jm->from); + JabberBuddyResource *jbr; + jb = jabber_buddy_find(jm->js, jm->from, TRUE); + jbr = jabber_buddy_find_resource(jb, jid->resource); + if(jabber_find_unnormalized_conv(jm->from, jm->js->gc->account)) { + from = g_strdup(jm->from); + GaimConversation *conv; + from = g_strdup_printf("%s@%s", jid->node, jid->domain); + conv = jabber_find_unnormalized_conv(from, jm->js->gc->account); + gaim_conversation_set_name(conv, jm->from); + from = g_strdup(jm->from); + from = g_strdup(jid->domain); + if(!jm->xhtml && !jm->body) { + if(jm->events & JABBER_MESSAGE_EVENT_COMPOSING) + serv_got_typing(jm->js->gc, from, 0, GAIM_TYPING); + serv_got_typing_stopped(jm->js->gc, from); + if(jm->events & JABBER_MESSAGE_EVENT_COMPOSING) + jbr->capabilities |= JABBER_CAP_COMPOSING; + g_free(jbr->thread_id); + jbr->thread_id = g_strdup(jbr->thread_id); + serv_got_im(jm->js->gc, from, jm->xhtml ? jm->xhtml : jm->body, 0, +static void handle_headline(JabberMessage *jm) + GString *body = g_string_new(""); + title = g_strdup_printf(_("Message from %s"), jm->from); + g_string_append(body, jm->xhtml); + g_string_append(body, jm->body); + for(etc = jm->etc; etc; etc = etc->next) { + xmlnode *x = etc->data; + const char *xmlns = xmlnode_get_attrib(x, "xmlns"); + if(xmlns && !strcmp(xmlns, "jabber:x:oob")) { + char *urltxt, *desctxt; + url = xmlnode_get_child(x, "url"); + desc = xmlnode_get_child(x, "desc"); + urltxt = xmlnode_get_data(url); + desctxt = xmlnode_get_data(desc); + /* I'm all about ugly hacks */ + if(body->len && !strcmp(body->str, jm->body)) + g_string_printf(body, "<a href='%s'>%s</a>", + g_string_append_printf(body, "<br/><a href='%s'>%s</a>", + gaim_notify_formatted(jm->js->gc, title, jm->subject ? jm->subject : title, + NULL, body->str, NULL, NULL); + g_string_free(body, TRUE); +static void handle_groupchat(JabberMessage *jm) + JabberID *jid = jabber_id_new(jm->from); + chat = jabber_chat_find(jm->js, jid->node, jid->domain); + gaim_conv_chat_set_topic(GAIM_CONV_CHAT(chat->conv), jid->resource, + if(!jm->xhtml && !jm->body) { + char *msg, *tmp, *tmp2; + tmp = gaim_escape_html(jm->subject); + tmp2 = gaim_markup_linkify(tmp); + msg = g_strdup_printf(_("%s has set the topic to: %s"), jid->resource, tmp2); + msg = g_strdup_printf(_("The topic is: %s"), tmp2); + gaim_conv_chat_write(GAIM_CONV_CHAT(chat->conv), "", msg, GAIM_MESSAGE_SYSTEM, jm->sent); + if(jm->xhtml || jm->body) { + serv_got_chat_in(jm->js->gc, chat->id, jid->resource, + jm->delayed ? GAIM_CONV_CHAT_DELAYED : 0, + jm->xhtml ? jm->xhtml : jm->body, jm->sent); + gaim_conv_chat_write(GAIM_CONV_CHAT(chat->conv), "", + jm->xhtml ? jm->xhtml : jm->body, + GAIM_MESSAGE_SYSTEM, jm->sent); +static void handle_groupchat_invite(JabberMessage *jm) + GHashTable *components; + JabberID *jid = jabber_id_new(jm->to); + components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + g_hash_table_replace(components, g_strdup("room"), g_strdup(jid->node)); + g_hash_table_replace(components, g_strdup("server"), g_strdup(jid->domain)); + g_hash_table_replace(components, g_strdup("handle"), + g_strdup(jm->js->user->node)); + g_hash_table_replace(components, g_strdup("password"), + g_strdup(jm->password)); + stripped = gaim_markup_strip_html(jm->body); + serv_got_chat_invite(jm->js->gc, jm->to, jm->from, stripped, components); +static void handle_error(JabberMessage *jm) + buf = g_strdup_printf(_("Message delivery to %s failed: %s"), + gaim_notify_formatted(jm->js->gc, _("Jabber Message Error"), _("Jabber Message Error"), buf, + jm->xhtml ? jm->xhtml : jm->body, NULL, NULL); +void jabber_message_parse(JabberStream *js, xmlnode *packet) + if(strcmp(packet->name, "message")) + jm = g_new0(JabberMessage, 1); + type = xmlnode_get_attrib(packet, "type"); + if(!strcmp(type, "normal")) + jm->type = JABBER_MESSAGE_NORMAL; + else if(!strcmp(type, "chat")) + jm->type = JABBER_MESSAGE_CHAT; + else if(!strcmp(type, "groupchat")) + jm->type = JABBER_MESSAGE_GROUPCHAT; + else if(!strcmp(type, "headline")) + jm->type = JABBER_MESSAGE_HEADLINE; + else if(!strcmp(type, "error")) + jm->type = JABBER_MESSAGE_ERROR; + jm->type = JABBER_MESSAGE_OTHER; + jm->type = JABBER_MESSAGE_NORMAL; + jm->from = g_strdup(xmlnode_get_attrib(packet, "from")); + jm->to = g_strdup(xmlnode_get_attrib(packet, "to")); + for(child = packet->child; child; child = child->next) { + if(child->type != XMLNODE_TYPE_TAG) + if(!strcmp(child->name, "subject")) { + jm->subject = xmlnode_get_data(child); + } else if(!strcmp(child->name, "thread")) { + jm->thread_id = xmlnode_get_data(child); + } else if(!strcmp(child->name, "body")) { + jm->body = xmlnode_to_str(child, NULL); + } else if(!strcmp(child->name, "html")) { + if(!jm->xhtml && child->data_sz > 0) + jm->xhtml = xmlnode_to_str(child, NULL); + } else if(!strcmp(child->name, "error")) { + const char *code = xmlnode_get_attrib(child, "code"); + char *text = xmlnode_get_data(child); + code_txt = g_strdup_printf(_(" (Code %s)"), code); + jm->error = g_strdup_printf("%s%s", text ? text : "", + code_txt ? code_txt : ""); + } else if(!strcmp(child->name, "x")) { + const char *xmlns = xmlnode_get_attrib(child, "xmlns"); + if(xmlns && !strcmp(xmlns, "jabber:x:event")) { + if(xmlnode_get_child(child, "composing")) + jm->events |= JABBER_MESSAGE_EVENT_COMPOSING; + } else if(xmlns && !strcmp(xmlns, "jabber:x:delay")) { + const char *timestamp = xmlnode_get_attrib(child, "stamp"); + jm->sent = gaim_str_to_time(timestamp, TRUE); + } else if(xmlns && !strcmp(xmlns, "jabber:x:conference") && + jm->type != JABBER_MESSAGE_GROUPCHAT_INVITE) { + const char *jid = xmlnode_get_attrib(child, "jid"); + jm->type = JABBER_MESSAGE_GROUPCHAT_INVITE; + jm->to = g_strdup(jid); + } else if(xmlns && !strcmp(xmlns, + "http://jabber.org/protocol/muc#user")) { + xmlnode *invite = xmlnode_get_child(child, "invite"); + xmlnode *reason, *password; + const char *jid = xmlnode_get_attrib(invite, "from"); + jm->from = g_strdup(jid); + if((reason = xmlnode_get_child(invite, "reason"))) { + jm->body = xmlnode_get_data(reason); + if((password = xmlnode_get_child(invite, "password"))) + jm->password = xmlnode_get_data(password); + jm->type = JABBER_MESSAGE_GROUPCHAT_INVITE; + jm->etc = g_list_append(jm->etc, child); + case JABBER_MESSAGE_NORMAL: + case JABBER_MESSAGE_CHAT: + case JABBER_MESSAGE_HEADLINE: + case JABBER_MESSAGE_GROUPCHAT: + case JABBER_MESSAGE_GROUPCHAT_INVITE: + handle_groupchat_invite(jm); + case JABBER_MESSAGE_ERROR: + case JABBER_MESSAGE_OTHER: + gaim_debug(GAIM_DEBUG_INFO, "jabber", + "Received message of unknown type: %s\n", type); + jabber_message_free(jm); +void jabber_message_send(JabberMessage *jm) + xmlnode *message, *child; + const char *type = NULL; + message = xmlnode_new("message"); + case JABBER_MESSAGE_NORMAL: + case JABBER_MESSAGE_CHAT: + case JABBER_MESSAGE_GROUPCHAT_INVITE: + case JABBER_MESSAGE_HEADLINE: + case JABBER_MESSAGE_GROUPCHAT: + case JABBER_MESSAGE_ERROR: + case JABBER_MESSAGE_OTHER: + xmlnode_set_attrib(message, "type", type); + xmlnode_set_attrib(message, "to", jm->to); + child = xmlnode_new_child(message, "thread"); + xmlnode_insert_data(child, jm->thread_id, -1); + if(jm->events || (!jm->body && !jm->xhtml && !jm->subject)) { + child = xmlnode_new_child(message, "x"); + xmlnode_set_attrib(child, "xmlns", "jabber:x:event"); + if(jm->events & JABBER_MESSAGE_EVENT_COMPOSING) + xmlnode_new_child(child, "composing"); + child = xmlnode_new_child(message, "subject"); + xmlnode_insert_data(child, jm->subject, -1); + child = xmlnode_new_child(message, "body"); + xmlnode_insert_data(child, jm->body, -1); + child = xmlnode_from_str(jm->xhtml, -1); + xmlnode_insert_child(message, child); + gaim_debug(GAIM_DEBUG_ERROR, "jabber", + "XHTML translation/validation failed, returning: %s\n", + jabber_send(jm->js, message); +int jabber_message_send_im(GaimConnection *gc, const char *who, const char *msg, + JabberBuddyResource *jbr; + resource = jabber_get_resource(who); + jb = jabber_buddy_find(gc->proto_data, who, TRUE); + jbr = jabber_buddy_find_resource(jb, resource); + jm = g_new0(JabberMessage, 1); + jm->js = gc->proto_data; + jm->type = JABBER_MESSAGE_CHAT; + jm->events = JABBER_MESSAGE_EVENT_COMPOSING; + jm->to = g_strdup(who); + if(jbr && jbr->thread_id) + jm->thread_id = jbr->thread_id; + buf = g_strdup_printf("<html xmlns='http://jabber.org/protocol/xhtml-im'><body xmlns='http://www.w3.org/1999/xhtml'>%s</body></html>", msg); + gaim_markup_html_to_xhtml(buf, &xhtml, &jm->body); + if(!jbr || jbr->capabilities & JABBER_CAP_XHTML) + jabber_message_send(jm); + jabber_message_free(jm); +int jabber_message_send_chat(GaimConnection *gc, int id, const char *msg) + chat = jabber_chat_find_by_id(js, id); + jm = g_new0(JabberMessage, 1); + jm->js = gc->proto_data; + jm->type = JABBER_MESSAGE_GROUPCHAT; + jm->to = g_strdup_printf("%s@%s", chat->room, chat->server); + buf = g_strdup_printf("<html xmlns='http://jabber.org/protocol/xhtml-im'><body xmlns='http://www.w3.org/1999/xhtml'>%s</body></html>", msg); + gaim_markup_html_to_xhtml(buf, &jm->xhtml, &jm->body); + jabber_message_send(jm); + jabber_message_free(jm); +int jabber_send_typing(GaimConnection *gc, const char *who, int typing) + JabberBuddyResource *jbr; + char *resource = jabber_get_resource(who); + jb = jabber_buddy_find(gc->proto_data, who, TRUE); + jbr = jabber_buddy_find_resource(jb, resource); + if(!jbr || !(jbr->capabilities & JABBER_CAP_COMPOSING)) + jm = g_new0(JabberMessage, 1); + jm->js = gc->proto_data; + jm->type = JABBER_MESSAGE_CHAT; + jm->to = g_strdup(who); + if(typing == GAIM_TYPING) + jm->events = JABBER_MESSAGE_EVENT_COMPOSING; + jabber_message_send(jm); + jabber_message_free(jm); + return JABBER_TYPING_NOTIFY_INT;