diff src/protocols/jabber/message.c @ 7014:67c4e9d39242

[gaim-migrate @ 7577] Here it is, the bulk of the new Jabber prpl. Left to do: - Implement registration - Implement password changing - Keep track of conversation threads (since I apparently have to) - Fix the bugs that always magically appear in code after I commit committer: Tailor Script <tailor@pidgin.im>
author Nathan Walp <nwalp@pidgin.im>
date Mon, 29 Sep 2003 15:23:19 +0000
parents
children c8bf2da398e3
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/jabber/message.c	Mon Sep 29 15:23:19 2003 +0000
@@ -0,0 +1,413 @@
+/*
+ * 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
+ *
+ */
+#include "internal.h"
+
+#include "debug.h"
+#include "html.h"
+#include "notify.h"
+#include "server.h"
+
+#include "buddy.h"
+#include "chat.h"
+#include "message.h"
+#include "xmlnode.h"
+
+#define JABBER_TYPING_NOTIFY_INT 15
+
+void jabber_message_free(JabberMessage *jm)
+{
+	if(jm->from)
+		g_free(jm->from);
+	if(jm->to)
+		g_free(jm->to);
+	if(jm->subject)
+		g_free(jm->subject);
+	if(jm->body)
+		g_free(jm->body);
+	if(jm->xhtml)
+		g_free(jm->xhtml);
+	if(jm->password)
+		g_free(jm->password);
+
+	g_free(jm);
+}
+
+void handle_chat(JabberMessage *jm)
+{
+	JabberID *jid = jabber_id_new(jm->from);
+	char *from;
+
+	JabberBuddy *jb;
+	JabberBuddyResource *jbr;
+
+	jb = jabber_buddy_find(jm->js, jm->from, TRUE);
+	jbr = jabber_buddy_find_resource(jb, jabber_get_resource(jm->from));
+
+	if(gaim_find_conversation_with_account(jm->from, jm->js->gc->account))
+		from = g_strdup(jm->from);
+	else if(jid->node)
+		from = g_strdup_printf("%s@%s", jid->node, jid->domain);
+	else
+		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);
+		else
+			serv_got_typing_stopped(jm->js->gc, from);
+	} else {
+		if(jbr && jm->events & JABBER_MESSAGE_EVENT_COMPOSING)
+			jbr->capabilities |= JABBER_CAP_COMPOSING;
+		serv_got_im(jm->js->gc, from, jm->xhtml ? jm->xhtml : jm->body, 0,
+				jm->sent);
+	}
+
+	g_free(from);
+	jabber_id_free(jid);
+}
+
+void handle_groupchat(JabberMessage *jm)
+{
+	JabberID *jid = jabber_id_new(jm->from);
+	JabberChat *chat = jabber_chat_find(jm->js, jid->node, jid->domain);
+
+	if(!chat)
+		return;
+
+	if(jm->subject)
+		gaim_chat_set_topic(GAIM_CHAT(chat->conv), jid->resource, jm->subject);
+
+	serv_got_chat_in(jm->js->gc, chat->id, jabber_get_resource(jm->from),
+			0, jm->xhtml ? jm->xhtml : jm->body, jm->sent);
+	jabber_id_free(jid);
+}
+
+void handle_groupchat_invite(JabberMessage *jm)
+{
+	GHashTable *components = g_hash_table_new_full(g_str_hash, g_str_equal,
+			g_free, g_free);
+	JabberID *jid = jabber_id_new(jm->to);
+
+	g_hash_table_replace(components, g_strdup("room"), jid->node);
+	g_hash_table_replace(components, g_strdup("server"), jid->node);
+	g_hash_table_replace(components, g_strdup("handle"), jm->js->user->node);
+	g_hash_table_replace(components, g_strdup("password"), jm->password);
+
+	jabber_id_free(jid);
+	serv_got_chat_invite(jm->js->gc, jm->to, jm->from, jm->body, components);
+}
+
+void handle_error(JabberMessage *jm)
+{
+	char *buf;
+
+	if(!jm->body)
+		return;
+
+	buf = g_strdup_printf(_("Message delivery to %s failed: %s"),
+			jm->from, jm->error);
+
+	gaim_notify_error(jm->js->gc, _("Jabber Message Error"), buf, jm->body);
+
+	g_free(buf);
+}
+
+void jabber_message_parse(JabberStream *js, xmlnode *packet)
+{
+	JabberMessage *jm;
+	const char *type;
+	xmlnode *child;
+
+	if(strcmp(packet->name, "message"))
+		return;
+
+	jm = g_new0(JabberMessage, 1);
+	jm->js = js;
+	jm->sent = time(NULL);
+
+	type = xmlnode_get_attrib(packet, "type");
+
+	if(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;
+		else
+			jm->type = JABBER_MESSAGE_OTHER;
+	} else {
+		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 != NODE_TYPE_TAG)
+			continue;
+
+		if(!strcmp(child->name, "subject")) {
+			if(!jm->subject)
+				jm->subject = xmlnode_get_data(child);
+		} else if(!strcmp(child->name, "body")) {
+			if(!jm->body)
+				jm->body = xmlnode_get_data(child);
+		} else if(!strcmp(child->name, "html") && child->child) {
+			/* check to see if the <html> actually contains anything,
+			 * otherwise we'll ignore it */
+			char *txt = xmlnode_get_data(child);
+			if(!jm->xhtml && txt)
+				jm->xhtml = xmlnode_to_str(child);
+			g_free(txt);
+		} else if(!strcmp(child->name, "error")) {
+			const char *code = xmlnode_get_attrib(child, "code");
+			char *code_txt = NULL;
+			char *text = xmlnode_get_data(child);
+
+			if(code)
+				code_txt = g_strdup_printf(_(" (Code %s)"), code);
+
+			if(!jm->error)
+				jm->error = g_strdup_printf("%s%s", text ? text : "",
+						code_txt ? code_txt : "");
+
+			g_free(code_txt);
+			g_free(text);
+		} 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");
+				if(timestamp)
+					jm->sent = str_to_time(timestamp);
+			} else if(xmlns && !strcmp(xmlns, "jabber:x:conference") &&
+					jm->type != JABBER_MESSAGE_GROUPCHAT_INVITE) {
+				const char *jid = xmlnode_get_attrib(child, "jid");
+				if(jid) {
+					jm->type = JABBER_MESSAGE_GROUPCHAT_INVITE;
+					g_free(jm->to);
+					jm->to = g_strdup(jid);
+				}
+			} else if(xmlns && !strcmp(xmlns,
+						"http://jabber.org/protocol/muc#user")) {
+				xmlnode *invite = xmlnode_get_child(child, "invite");
+				if(invite) {
+					xmlnode *reason, *password;
+					const char *jid = xmlnode_get_attrib(child, "from");
+					g_free(jm->to);
+					jm->to = jm->from;
+					jm->from = g_strdup(jid);
+					if((reason = xmlnode_get_child(invite, "reason"))) {
+						g_free(jm->body);
+						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;
+				}
+			}
+		}
+	}
+
+	switch(jm->type) {
+		case JABBER_MESSAGE_NORMAL:
+		case JABBER_MESSAGE_CHAT:
+		case JABBER_MESSAGE_HEADLINE:
+			handle_chat(jm);
+			break;
+		case JABBER_MESSAGE_GROUPCHAT:
+			handle_groupchat(jm);
+			break;
+		case JABBER_MESSAGE_GROUPCHAT_INVITE:
+			handle_groupchat_invite(jm);
+			break;
+		case JABBER_MESSAGE_ERROR:
+			handle_error(jm);
+			break;
+		case JABBER_MESSAGE_OTHER:
+			gaim_debug(GAIM_DEBUG_INFO, "jabber",
+					"Received message of unknown type: %s\n", type);
+			break;
+	}
+	jabber_message_free(jm);
+}
+
+void jabber_message_send(JabberMessage *jm)
+{
+	xmlnode *message, *child;
+	const char *type = NULL;
+
+	message = xmlnode_new("message");
+
+	switch(jm->type) {
+		case JABBER_MESSAGE_NORMAL:
+			type = "normal";
+			break;
+		case JABBER_MESSAGE_CHAT:
+		case JABBER_MESSAGE_GROUPCHAT_INVITE:
+			type = "chat";
+			break;
+		case JABBER_MESSAGE_HEADLINE:
+			type = "headline";
+			break;
+		case JABBER_MESSAGE_GROUPCHAT:
+			type = "groupchat";
+			break;
+		case JABBER_MESSAGE_ERROR:
+			type = "error";
+			break;
+		case JABBER_MESSAGE_OTHER:
+			type = NULL;
+			break;
+	}
+
+	if(type)
+		xmlnode_set_attrib(message, "type", type);
+
+	xmlnode_set_attrib(message, "to", jm->to);
+
+	if(jm->events || (!jm->body && !jm->xhtml)) {
+		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");
+	}
+
+	if(jm->subject) {
+		child = xmlnode_new_child(message, "subject");
+		xmlnode_insert_data(child, jm->subject, -1);
+	}
+
+	if(jm->body) {
+		child = xmlnode_new_child(message, "body");
+		xmlnode_insert_data(child, jm->body, -1);
+	}
+
+	if(jm->xhtml) {
+		child = xmlnode_from_str(jm->xhtml, -1);
+		if(child) {
+			xmlnode_insert_child(message, child);
+		} else {
+			gaim_debug(GAIM_DEBUG_ERROR, "jabber",
+					"XHTML translation/validation failed, returning: %s\n",
+					jm->xhtml);
+		}
+	}
+
+	jabber_send(jm->js, message);
+
+	xmlnode_free(message);
+}
+
+int jabber_message_send_im(GaimConnection *gc, const char *who, const char *msg,
+		GaimImFlags flags)
+{
+	JabberMessage *jm;
+	JabberBuddy *jb;
+	JabberBuddyResource *jbr;
+	char *buf;
+	char *xhtml, *plain;
+
+	if(!who || !msg)
+		return 0;
+
+	jb = jabber_buddy_find(gc->proto_data, who, TRUE);
+	jbr = jabber_buddy_find_resource(jb, jabber_get_resource(who));
+
+	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);
+
+	buf = g_strdup_printf("<html xmlns='http://jabber.org/protocol/xhtml-im'><body>%s</body></html>", msg);
+
+	html_to_xhtml(buf, &xhtml, &plain);
+	g_free(buf);
+
+	jm->body = plain;
+	if(!jbr || jbr->capabilities & JABBER_CAP_XHTML)
+		jm->xhtml = xhtml;
+	else
+		g_free(xhtml);
+
+	jabber_message_send(jm);
+	jabber_message_free(jm);
+	return 1;
+}
+
+int jabber_message_send_chat(GaimConnection *gc, int id, const char *message)
+{
+	JabberChat *chat;
+	JabberMessage *jm;
+	JabberStream *js = gc->proto_data;
+
+	if(!message)
+		return 0;
+
+	chat = jabber_chat_find_by_id(js, id);
+
+	jm = g_new0(JabberMessage, 1);
+	jm->js = gc->proto_data;
+	jm->type = JABBER_MESSAGE_CHAT;
+	jm->to = g_strdup_printf("%s@%s", chat->room, chat->server);
+
+	html_to_xhtml(message, NULL, &jm->body);
+
+	jabber_message_send(jm);
+	jabber_message_free(jm);
+	return 1;
+}
+
+int jabber_send_typing(GaimConnection *gc, const char *who, int typing)
+{
+	JabberMessage *jm;
+	JabberBuddy *jb;
+	JabberBuddyResource *jbr;
+
+	jb = jabber_buddy_find(gc->proto_data, who, TRUE);
+	jbr = jabber_buddy_find_resource(jb, jabber_get_resource(who));
+
+	if(jbr && !(jbr->capabilities & JABBER_CAP_COMPOSING))
+		return 0;
+
+	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;
+}
+