Re: Mail reader app (Re: Is Beagle indexing my email?)



My *preference* would be if Evolution were modified to support reading
mails from non ~/.evolution locations, so that we can launch

        evolution file:///home/nat/old-mail/Maildir/cur/09238u092384098
        
and Evolution would pop up a mail-viewer window, allowing me to respond,
forward, etc.  This way I have one interface to mail on my system (as an
Evolution user).

It is a secondary option to create a standalone viewer.  This will be
useful to people who aren't exposed to Evolution itself.

On Wed, 2005-05-18 at 11:50 +0200, Sigurd Gartmann wrote:
> Beagle will be a killer app. I saw your presentation at last
> year's GUADEC, and have done a presentation about "metadata
> search systems on the desktop" for the digital library group at
> the university.

Very cool!

> You probably want more than this. What do you want?

Well, we have the beagle-imlog-viewer in Beagle; I guess a
beagle-mail-viewer would run along similar lines. 

The challenge is, once you can view mails, you suddenly want other
functionality, including

        - replying to them,
        - forwarding them,
        - viewing or saving their attachments,
        
and so I think that this project is a bit of a slippery slope.  That's
why I recommend making Evolution work as a mail viewer.

If you were going to write a mail viewer, it'd be nice if it were done
in Mono, in the Beagle tree.  I started writing such a thing a while
ago, but didn't get very far (as you can see).  Maybe someone can pick
it up.

Nat

//
// MailViewer.cs
//
// Nat Friedman <nat novell com>
//
// Copyright (C) 2005 Novell, Inc.
//

using System;
using System.Collections;
using System.IO;
using Gtk;
using Glade;
using System.Text;
using System.Xml;
using Mono.Posix;

using Beagle.Util;

namespace Beagle {

	public class Driver {
		
		static void Usage (string error)
		{

			if (error != null)
				Console.WriteLine ("Error: " + error + "\n\n");

			string usage =
				"beagle-mailviewer: A simple tool to display a mail message.\n" +
				"Web page: http://www.gnome.org/projects/beagle\n"; +
				"Copyright (C) 2004 Novell, Inc.\n\n";

			usage +=
				"Usage: beagle-mailviewer [OPTIONS] <mbox path> <byte offset to mail>\n\n" +
				"Options:\n" +
				"  --help\t\t\tPrint this usage message.\n";

			Console.WriteLine (usage);
			System.Environment.Exit (0);
		}

		static string path;
		static long offset;
		
		public static void Main (string[] args)
		{
			if (args.Length < 2)
				Usage ("Required arguments missing");

			if (! File.Exists (args [0]))
				Usage ("Mailbox file " + args [0] + " does not exist");

			Application.Init ();
			GeckoUtils.Init ();
			GeckoUtils.SetSystemFonts ();
			GMime.Global.Init ();

			path = args [0];
			offset = System.Convert.ToInt64 (args [1]);

			GLib.Idle.Add (new GLib.IdleHandler (DoIt));

			Application.Run ();
		}

		private static bool DoIt ()
		{
			MailViewerWindow mvw = new MailViewerWindow (path, offset);
			return false;
		}
	}

	public class MailViewerWindow {
		[Widget] Window win;
		private Gecko.WebControl gecko;

		string mail_body = "";

		public MailViewerWindow (string path, long offset) {

			GMime.Message message = ReadMessage (path, offset);
			if (message == null) {
				// FIXME: We should pop up a window
				Console.WriteLine ("Could not read message from {0} at byte offset {1}.", path, offset);
				return;
			}

			ConstructWindow ();

			RenderMessage (message);
		}

		GMime.Message ReadMessage (string path, long offset)
		{
			int mbox_fd = Syscall.open (path, OpenFlags.O_RDONLY);
				
			GMime.StreamFs mbox_stream = new GMime.StreamFs (mbox_fd);
			mbox_stream.Seek ((int) offset);
			GMime.Parser mbox_parser = new GMime.Parser (mbox_stream);
			mbox_parser.ScanFrom = true;

			GMime.Message message = mbox_parser.ConstructMessage ();

			mbox_stream.Close ();

			if (message.Headers == null || message.Headers == "") 
				return null;

			return message;
		}

		void RenderMessage (GMime.Message message)
		{
			string sender = Htmlify (message.Sender);
			string to = Htmlify (message.GetRecipientsAsString (GMime.Message.RecipientType.To));

			string cc = message.GetRecipientsAsString (GMime.Message.RecipientType.Cc);
			if (cc == null)
				cc = "";
			else
				cc = "<b>Cc:</b> " + Htmlify (cc) + "<br>";

			string subject = Htmlify (message.Subject);

			GetMessageBody (message);
			
			string html = String.Format ("<html><body>" +
						     "<b>From:</b> {0}<br>" +
						     "<b>To:</b> {1}<br>" +
						     "{2}" +
						     "<b>Subject:</b> {3}" +
						     "<hr>" +
						     "{4}" +
						     "</body></html>",
						     sender,
						     to,
						     cc,
					     subject,
						     mail_body);
				       
				       
			gecko.RenderData(html, "file:///tmp/foo", "text/html");

			Console.WriteLine (html);
		}

		string Htmlify (string s)
		{
			return Beagle.Util.StringFu.EscapeStringForHtml (s, false);
		}

		void GetMessageBody (GMime.Message message)
		{
			this.OnEachPart (message.MimePart);
		}

		void OnEachPart (GMime.Object part)
		{
			if (part is GMime.MessagePart) {
				Console.WriteLine ("** message part: " + part.ToString ());
				Console.WriteLine ("fe ** mp------------------------------------------------------");

				GMime.MessagePart msg_part = (GMime.MessagePart) part;
				msg_part.Message.ForeachPart (new GMime.PartFunc (this.OnEachPart));
			} else if (part is GMime.Multipart) {
				Console.WriteLine ("mp multipart: " + part.ToString ());
				Console.WriteLine ("mp ------------------------------------------------------");

				GMime.Multipart multipart = (GMime.Multipart) part;
				multipart.ForeachPart (new GMime.PartFunc (this.OnEachPart));
				Console.WriteLine ("femp ------------------------------------------------------");
			} else {
				MemoryStream stream = new MemoryStream (part.GetData ());
				StreamReader reader  = new StreamReader (stream);
				Console.WriteLine ("CT: {0} ID: {1}", part.ContentType, part.ContentId);
				Console.WriteLine ("String: " + reader.ReadToEnd ());
				Console.WriteLine ("------------------------------------------------------");

				mail_body += System.Text.Encoding.ASCII.GetString (part.GetData ());
				Console.WriteLine ("Body: " + mail_body);
			}
		}

		Gtk.AccelGroup accel_group;
		GlobalKeybinder global_keys;

		void ConstructWindow ()
		{
			win = new Gtk.Window ("Beagle Mail Viewer");
			win.SetDefaultSize (600, 500);

			Gtk.ScrolledWindow sw = new ScrolledWindow ();
			sw.HscrollbarPolicy = Gtk.PolicyType.Automatic;
			sw.VscrollbarPolicy = Gtk.PolicyType.Automatic;

			gecko = new Gecko.WebControl ();
			gecko.Show ();
		
			sw.Add (gecko);
			win.Add (sw);
				
			accel_group = new Gtk.AccelGroup ();
			win.AddAccelGroup (accel_group);
			global_keys = new GlobalKeybinder (accel_group);

			// Close window (Ctrl-W)
			global_keys.AddAccelerator (new EventHandler (this.CloseWindow),
						    (uint) Gdk.Key.w, 
						    Gdk.ModifierType.ControlMask,
						    Gtk.AccelFlags.Visible);

			// Close window (Escape)
			global_keys.AddAccelerator (new EventHandler (this.CloseWindow),
						    (uint) Gdk.Key.Escape, 
						    0,
						    Gtk.AccelFlags.Visible);


			win.ShowAll ();
		}
		
		private void CloseWindow (object o, EventArgs args)
		{
			Application.Quit ();
		}
	}
}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]