diff -urN evolution-1.4.5.orig/mail/dashboard-frontend.c evolution-1.4.5/mail/dashboard-frontend.c
--- evolution-1.4.5.orig/mail/dashboard-frontend.c	1970-01-01 12:00:00.000000000 +1200
+++ evolution-1.4.5/mail/dashboard-frontend.c	2003-12-06 15:24:52.000000000 +1300
@@ -0,0 +1,443 @@
+#ifndef __DASHBOARD_FRONTEND_H__
+#define __DASHBOARD_FRONTEND_H__
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <glib.h>
+
+#if GLIB_CHECK_VERSION (2,0,0)
+#include <glib/giochannel.h>
+#endif
+
+#define DASHBOARD_PORT 5913
+#define NATMIN(a,b) ((a) < (b) ? (a) : (b))
+
+/*
+ * Open a connection to the dashboard.  We never block and at
+ * the first sign of a problem we bail.
+ */
+static int
+dashboard_connect_with_timeout (int  *fd,
+				long  timeout_usecs)
+{
+	struct sockaddr_in  sock;
+	struct timeval      timeout;
+	fd_set write_fds;
+
+	*fd = socket (PF_INET, SOCK_STREAM, 0);
+	if (*fd < 0) {
+		perror ("Dashboard: socket");
+		return 0;
+	}
+
+	/*
+	 * Set the socket to be non-blocking so that connect ()
+	 * doesn't block.
+	 */
+	if (fcntl (*fd, F_SETFL, O_NONBLOCK) < 0) {
+		perror ("Dashboard: setting O_NONBLOCK");
+
+		if (close(*fd) < 0)
+			perror ("Dashboard: closing socket (1)");
+		
+		return 0;
+	}
+
+	bzero ((char *) &sock, sizeof (sock));
+	sock.sin_family      = AF_INET;
+	sock.sin_port        = htons (DASHBOARD_PORT);
+	sock.sin_addr.s_addr = inet_addr ("127.0.0.1");
+
+	timeout.tv_sec = 0;
+	timeout.tv_usec = timeout_usecs;
+
+	while (1) {
+
+		/*
+		 * Try to connect.
+		 */
+		if (connect (*fd, (struct sockaddr *) &sock,
+			     sizeof (struct sockaddr_in)) < 0) {
+
+			if (errno != EAGAIN &&
+			    errno != EINPROGRESS) {
+				perror ("Dashboard: connect");
+
+				if (close(*fd) < 0)
+					perror ("Dashboard: closing socket (2)");
+
+				return 0;
+			}
+				
+		} else
+			return 1;
+
+		/*
+		 * We couldn't connect, so we select on the fd and
+		 * wait for the timer to run out, or for the fd to be
+		 * ready.
+		 */
+		FD_ZERO (&write_fds);
+		FD_SET (*fd, &write_fds);
+
+		while (select (getdtablesize (), NULL, &write_fds, NULL, &timeout) < 0) {
+			if (errno != EINTR) {
+				perror ("Dashboard: select");
+
+				if (close(*fd) < 0)
+					perror ("Dashboard: closing socket (3)");
+		
+				return 0;
+			}
+		}
+
+		if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
+			fprintf (stderr, "Dashboard: Connection timed out.\n");
+
+			if (close(*fd) < 0)
+				perror ("Dashboard: closing socket (4)");
+		
+			return 0;
+		}
+		
+	}
+
+	return 1;
+}
+
+typedef struct {
+	char *rawcluepacket;
+	int bytes_written;
+} CluepacketInfo;
+
+#if GLIB_CHECK_VERSION(2,0,0)
+
+static gboolean
+cluepacket_write_cb (GIOChannel   *channel,
+		     GIOCondition  cond,
+		     gpointer      user_data)
+{
+	CluepacketInfo *info = user_data;
+	GIOError err;
+	int total_bytes;
+
+	total_bytes = strlen (info->rawcluepacket);
+
+	do {
+		int b;
+
+		err = g_io_channel_write (channel,
+					  info->rawcluepacket + info->bytes_written,
+					  total_bytes - info->bytes_written,
+					  &b);
+		info->bytes_written += b;
+	} while (info->bytes_written < total_bytes && err == G_IO_ERROR_NONE);
+
+	if (err == G_IO_ERROR_NONE) {
+		/* We're all done sending */
+		fprintf (stderr, "Dashboard: Sent.\n");
+		goto cleanup;
+	}
+
+	if (err == G_IO_ERROR_AGAIN) {
+		/* Hand control back to the main loop */
+		return TRUE;
+	}
+
+	/* Otherwise... */
+	fprintf (stderr, "Dashboard: Error trying to send cluepacket.\n");
+
+cleanup:
+	g_io_channel_close (channel);
+	g_free (info->rawcluepacket);
+	g_free (info);
+
+	return FALSE;
+}
+
+static void
+dashboard_send_raw_cluepacket (const char *rawcluepacket)
+{
+	int fd;
+	GIOChannel *channel;
+	CluepacketInfo *info;
+
+	fprintf (stderr, "Dashboard: Sending cluepacket...\n");
+
+	/* Connect. */
+	if (! dashboard_connect_with_timeout (&fd, 200000))
+		return;
+
+	channel = g_io_channel_unix_new (fd);
+
+	info = g_new0 (CluepacketInfo, 1);
+	info->rawcluepacket = g_strdup (rawcluepacket);
+
+	g_io_add_watch (channel,
+			G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+			cluepacket_write_cb,
+			info);
+
+	g_io_channel_unref (channel);
+}
+
+#endif /* GLIB_CHECK_VERSION (2,0,0) */
+
+/*
+ * Sends a raw cluepacket to the dashboard.
+ */
+static void
+dashboard_send_raw_cluepacket_sync (const char *rawcluepacket)
+{
+	int                 fd;
+	int                 total_bytes;
+	int                 bytes_written;
+	const char         *p;
+	int                 eagain_count;
+
+	fprintf (stderr, "Dashboard: Sending cluepacket...\n");
+	fprintf (stderr, "Cluepacket: %s\n", rawcluepacket);
+
+	/* Connect. */
+	if (! dashboard_connect_with_timeout (&fd, 200000))
+		return;
+
+	/* Write out the cluepacket */
+	total_bytes = strlen (rawcluepacket);
+	bytes_written = 0;
+	p = rawcluepacket;
+	eagain_count = 0;
+	while (bytes_written < total_bytes) {
+		int b;
+
+		b = write (fd, p, total_bytes - bytes_written);
+		if (b < 0) {
+			fprintf (stderr, "Dashboard: Error writing: %s\n", strerror (errno));
+
+			if (errno != EAGAIN && errno != EWOULDBLOCK) {
+				close (fd);
+				return;
+			}
+
+			eagain_count ++;
+
+			if (eagain_count > 10) {
+				close (fd);
+				return;
+			}
+		}
+
+		bytes_written += b;
+		p += b;
+	}
+
+	close (fd);
+
+	fprintf (stderr, "Dashboard: Sent.\n");
+}
+
+#if !GLIB_CHECK_VERSION (2,0,0)
+static char *
+lame_xml_quote (const char *str)
+{
+	char       *retval;
+	const char *p;
+	char       *q;
+
+	if (str == NULL || strlen (str) == 0)
+		return g_strdup ("");
+
+	retval = g_new (char, strlen (str) * 3);
+
+	q = retval;
+	for (p = str; *p != '\0'; p ++) {
+		switch (*p) {
+
+		case '<':
+			*q ++ = '&';
+			*q ++ = 'l';
+			*q ++ = 't';
+			*q ++ = ';';
+			break;
+
+		case '>':
+			*q ++ = '&';
+			*q ++ = 'g';
+			*q ++ = 't';
+			*q ++ = ';';
+			break;
+			
+		case '&':
+			*q ++ = '&';
+			*q ++ = 'a';
+			*q ++ = 'm';
+			*q ++ = 'p';
+			*q ++ = ';';
+			break;
+		default:
+			*q ++ = *p;
+			break;
+		}
+	}
+
+	*q = '\0';
+
+	return retval;
+}
+#endif
+
+static char *
+dashboard_xml_quote (const char *str)
+{
+#if GLIB_CHECK_VERSION (2,0,0)
+	return g_markup_escape_text (str, strlen (str));
+#else
+	return lame_xml_quote (str);
+#endif
+}
+
+static char *
+dashboard_build_clue (const char *text,
+		      const char *type,
+		      int         relevance)
+{
+	char *text_xml;
+	char *clue;
+
+	if (text == NULL || strlen (text) == 0)
+		return g_strdup ("");
+
+	text_xml = dashboard_xml_quote (text);
+
+	clue = g_strdup_printf ("    <Clue Type=\"%s\" Relevance=\"%d\">%s</Clue>\n",
+				type, relevance, text_xml);
+
+	g_free (text_xml);
+
+	return clue;
+}
+
+static char *
+dashboard_build_cluepacket_from_cluelist (const char *frontend,
+					  gboolean    focused,
+					  const char *context,
+					  GList      *clues)
+{
+	char  *cluepacket;
+	char  *new_cluepacket;
+	GList *l;
+
+	g_return_val_if_fail (frontend != NULL, NULL);
+	g_return_val_if_fail (clues    != NULL, NULL);
+
+	cluepacket = g_strdup_printf (
+		"<CluePacket>\n"
+		"    <Frontend>%s</Frontend>\n"
+		"    <Context>%s</Context>\n"
+		"    <Focused>%s</Focused>\n",
+		frontend,
+		context,
+		focused ? "true" : "false");
+
+	for (l = clues; l != NULL; l = l->next) {
+		const char *clue = (const char *) l->data;
+
+		new_cluepacket = g_strconcat (cluepacket, clue, NULL);
+		g_free (cluepacket);
+
+		cluepacket = new_cluepacket;
+	}
+
+	new_cluepacket = g_strconcat (cluepacket, "</CluePacket>\n", NULL);
+	g_free (cluepacket);
+
+	cluepacket = new_cluepacket;
+
+	return cluepacket;
+}
+
+static char *
+dashboard_build_cluepacket_v (const char *frontend,
+			      gboolean    focused,
+			      const char *context,
+			      va_list     args)
+{
+	char    *cluep;
+	GList   *clue_list;
+	char    *retval;
+
+	g_return_val_if_fail (frontend != NULL, NULL);
+
+	cluep     = va_arg (args, char *);
+	clue_list = NULL;
+	while (cluep) {
+		clue_list = g_list_append (clue_list, cluep);
+		cluep = va_arg (args, char *);
+	}
+
+	retval = dashboard_build_cluepacket_from_cluelist (frontend, focused, context, clue_list);
+
+	g_list_free (clue_list);
+
+	return retval;
+}
+
+static char *
+dashboard_build_cluepacket (const char *frontend,
+			    gboolean    focused,
+			    const char *context,
+			    ...)
+{
+	char    *retval;
+	va_list  args;
+
+	g_return_val_if_fail (frontend != NULL, NULL);
+
+	va_start (args, context);
+
+	retval = dashboard_build_cluepacket_v (frontend, focused, context, args);
+
+	va_end (args);
+
+	return retval;
+}
+
+
+static char *
+dashboard_build_cluepacket_then_free_clues (const char *frontend,
+					    gboolean    focused,
+					    const char *context,
+					    ...)
+{
+	char    *retval;
+	char    *cluep;
+	va_list  args;
+
+	g_return_val_if_fail (frontend != NULL, NULL);
+
+	/* Build the cluepacket */
+	va_start (args, context);
+	retval = dashboard_build_cluepacket_v (frontend, focused, context, args);
+	va_end (args);
+
+	/* Free the clues */
+	va_start (args, context);
+	cluep = va_arg (args, char *);
+	while (cluep) {
+		g_free (cluep);
+		cluep = va_arg (args, char *);
+	}
+
+	va_end (args);
+
+	return retval;
+}
+
+#endif /* ! __DASHBOARD_FRONTEND_H__ */
diff -urN evolution-1.4.5.orig/mail/mail-display.c evolution-1.4.5/mail/mail-display.c
--- evolution-1.4.5.orig/mail/mail-display.c	2003-09-12 04:20:32.000000000 +1200
+++ evolution-1.4.5/mail/mail-display.c	2003-12-06 15:24:29.000000000 +1300
@@ -82,6 +82,8 @@
 
 #include "camel/camel-data-cache.h"
 
+#include "dashboard-frontend.c"
+
 #define d(x)
 
 struct _MailDisplayPrivate {
@@ -1937,6 +1939,100 @@
 	mail_display_render (md, md->html, reset_scroll);
 }
 
+static void dashboard_send_mail_cluepacket (MailDisplay *md);
+
+static gboolean
+dashboard_focus_in_cb (GtkWidget     *widget,
+		       GdkEventFocus *event,
+		       gpointer       user_data)
+{
+	MailDisplay *md = user_data;
+
+	if (! md)
+		return FALSE;
+
+	dashboard_send_mail_cluepacket (md);
+
+	return TRUE;
+}
+
+static gboolean
+dashboard_do_we_have_focus (MailDisplay *md)
+{
+	GtkWidget *w;
+	GtkWidget *focus;
+
+	for (w = GTK_WIDGET (md); w->parent != NULL; w = w->parent);
+	focus = GTK_WINDOW (w)->focus_widget;
+
+	/* Listen for focus events on this widget if we don't already. */
+	if (! g_object_get_data (G_OBJECT (w), "dashboard_focus")) {
+
+		g_signal_connect_after (G_OBJECT (w), "focus_in_event",
+					G_CALLBACK (dashboard_focus_in_cb),
+					md);
+
+		g_object_set_data (G_OBJECT (w), "dashboard_focus",
+				   (gpointer) TRUE);
+	}
+	
+	/* Check for focus. */
+	if (GTK_WIDGET_HAS_FOCUS (focus))
+		return TRUE;
+
+	return FALSE;
+}
+
+static void
+dashboard_send_mail_cluepacket (MailDisplay *md)
+{
+	CamelMedium                *medium = md->current_message;
+	CamelMimeMessage           *message = (CamelMimeMessage *) medium;
+	CamelDataWrapper           *dw;
+	const CamelInternetAddress *sender;
+	const char                 *sender_name  = NULL;
+	const char                 *sender_email = NULL;
+	const char                 *subject      = NULL;
+	char                       *body         = NULL;
+	char                       *context;
+	char                       *cluepacket;
+
+	if (md == NULL || md->current_message == NULL || md->destroyed)
+		return;
+
+	/* Get the sender's name and email address */
+	sender = camel_mime_message_get_from (message);
+	camel_internet_address_get (sender, 0, &sender_name, &sender_email);
+
+	/* Get the subject */
+	subject = camel_mime_message_get_subject (message);
+
+	/* Get the message body (no attachments yet) */
+	dw   = camel_medium_get_content_object (CAMEL_MEDIUM (message));
+	body = mail_get_message_body (dw, TRUE, FALSE);
+
+	/* Build the cluepacket */
+	context = g_strdup_printf ("Folder %s (%p)",
+				   md->folder->full_name ? md->folder->full_name : "",
+				   md);
+
+	cluepacket = dashboard_build_cluepacket_then_free_clues (
+		"Evolution Mail",
+		dashboard_do_we_have_focus (md),
+		context,
+		dashboard_build_clue (sender_name,  "full_name", 10),
+		dashboard_build_clue (sender_email, "email",     10),
+		dashboard_build_clue (subject,      "textblock", 10),
+		dashboard_build_clue (body,         "textblock", 10),
+		NULL);
+	g_free (context);
+
+	/* Send it off to the dashboard */
+	dashboard_send_raw_cluepacket (cluepacket);
+
+	g_free (cluepacket);
+	g_free (body);
+}
 
 /**
  * mail_display_set_message:
@@ -1985,9 +2081,12 @@
 		md->info = NULL;
 		md->folder = NULL;
 	}
-	
+
 	g_datalist_init (md->data);
 	mail_display_redisplay (md, TRUE);
+
+	if (folder && info && medium)
+		dashboard_send_mail_cluepacket (md);
 }
 
 /**
