3
using System.Collections;
8
using System.Runtime.InteropServices;
9
using System.Diagnostics;
12
using Mono.Unix.Native;
16
// TODO: Indent everything in this namespace in a seperate commit
17
namespace Tomboy.Evolution
22
// Cache of account URLs to account UIDs
23
static Hashtable source_url_to_uid;
25
static GConf.Client gc;
26
static GConf.NotifyEventHandler changed_handler;
31
gc = new GConf.Client ();
33
changed_handler = new GConf.NotifyEventHandler (OnAccountsSettingChanged);
34
gc.AddNotify ("/apps/evolution/mail/accounts", changed_handler);
36
source_url_to_uid = new Hashtable ();
37
SetupAccountUrlToUidMap ();
40
Logger.Log ("Evolution: Error reading accounts: {0}", e);
44
static void OnAccountsSettingChanged (object sender, GConf.NotifyEventArgs args)
46
SetupAccountUrlToUidMap ();
49
static void SetupAccountUrlToUidMap ()
51
source_url_to_uid.Clear ();
53
ICollection accounts = (ICollection) gc.Get ("/apps/evolution/mail/accounts");
55
foreach (string xml in accounts) {
56
XmlDocument xmlDoc = new XmlDocument ();
60
XmlNode account = xmlDoc.SelectSingleNode ("//account");
66
foreach (XmlAttribute attr in account.Attributes) {
67
if (attr.Name == "uid") {
76
XmlNode source_url = xmlDoc.SelectSingleNode ("//source/url");
77
if (source_url == null || source_url.InnerText == null)
81
Uri uri = new Uri (source_url.InnerText);
82
source_url_to_uid [uri] = uid;
83
} catch (System.UriFormatException) {
85
"Evolution: Unable to parse account source URI \"{0}\"",
86
source_url.InnerText);
92
// Duplicates evoluion/camel/camel-url.c:camel_url_encode and
93
// escapes ";?" as well.
95
static string CamelUrlEncode (string uri_part)
97
// The characters Camel encodes
98
const string camel_escape_chars = " \"%#<>{}|\\^[]`;?";
100
StringBuilder builder = new StringBuilder (null);
101
foreach (char c in uri_part) {
102
if (camel_escape_chars.IndexOf (c) > -1)
103
builder.Append (Uri.HexEscape (c));
108
return builder.ToString ();
113
// evolution/mail/mail-config.c:mail_config_get_account_by_source_url...
115
static string FindAccountUidForUri (Uri uri)
117
if (source_url_to_uid == null)
120
string account_uid = null;
122
foreach (Uri source_uri in source_url_to_uid.Keys) {
123
if (uri.Scheme != source_uri.Scheme)
128
// FIXME: check "authmech" matches too
129
switch (uri.Scheme) {
133
match = (uri.PathAndQuery == source_uri.PathAndQuery) &&
134
(uri.Authority == source_uri.Authority) &&
135
(uri.UserInfo == source_uri.UserInfo);
142
// FIXME: Do some path canonicalization here?
143
match = (uri.PathAndQuery == source_uri.PathAndQuery) &&
144
(uri.Authority == source_uri.Authority) &&
145
(uri.UserInfo == source_uri.UserInfo);
154
match = (uri.Authority == source_uri.Authority) &&
155
(uri.UserInfo == source_uri.UserInfo);
160
account_uid = (string) source_url_to_uid [source_uri];
161
Logger.Log ("Evolution: Matching account '{0}'...",
171
// Duplicates evolution/mail/em-utils.c:em_uri_from_camel...
173
public static string EmailUriFromDropUri (string drop_uri)
175
if (drop_uri.StartsWith ("email:"))
178
Uri uri = new Uri (drop_uri);
179
string account_uid = null;
182
if (drop_uri.StartsWith ("vfolder:"))
183
account_uid = "vfolder@local";
185
account_uid = FindAccountUidForUri (uri);
187
if (account_uid == null)
188
account_uid = "local@local";
191
switch (uri.Scheme) {
197
// These schemes keep the folder as the URI fragment
201
path = uri.AbsolutePath;
205
if (path != string.Empty) {
206
// Skip leading '/' or '#'...
207
path = path.Substring (1);
208
path = CamelUrlEncode (path);
211
return string.Format ("email://{0}/{1}", account_uid, path);
214
public static string EmailUriFromDropUri (string drop_uri, string uid)
216
string email_uri = EmailUriFromDropUri (drop_uri);
218
if (email_uri == null)
221
return string.Format ("{0};uid={1}", email_uri, uid);
225
public class EmailLink : DynamicNoteTag
232
public override void Initialize (string element_name)
234
base.Initialize (element_name);
236
Underline = Pango.Underline.Single;
240
Image = new Gdk.Pixbuf (null, "stock_mail.png");
243
public string EmailUri
245
get { return (string) Attributes ["uri"]; }
246
set { Attributes ["uri"] = value; }
249
protected override bool OnActivate (NoteEditor editor,
253
Process p = new Process ();
254
p.StartInfo.FileName = "evolution";
255
p.StartInfo.Arguments = EmailUri;
256
p.StartInfo.UseShellExecute = false;
260
} catch (Exception e) {
261
string message = String.Format ("Error running Evolution: {0}",
263
Logger.Log (message);
264
HIGMessageDialog dialog =
265
new HIGMessageDialog (editor.Toplevel as Gtk.Window,
266
Gtk.DialogFlags.DestroyWithParent,
267
Gtk.MessageType.Info,
269
Catalog.GetString ("Cannot open email"),
280
public class EvolutionNoteAddin : NoteAddin
282
// Used in the two-phase evolution drop handling.
284
ArrayList subject_list;
286
static EvolutionNoteAddin ()
291
public override void Initialize ()
293
if (!Note.TagTable.IsDynamicTagRegistered ("link:evo-mail")) {
294
Note.TagTable.RegisterDynamicTag ("link:evo-mail", typeof (EmailLink));
298
Gtk.TargetList TargetList
301
return Gtk.Drag.DestGetTargetList (Window.Editor);
305
public override void Shutdown ()
308
TargetList.Remove (Gdk.Atom.Intern ("x-uid-list", false));
311
public override void OnNoteOpened ()
313
TargetList.Add (Gdk.Atom.Intern ("x-uid-list", false), 0, 99);
314
Window.Editor.DragDataReceived += OnDragDataReceived;
317
[DllImport("libgobject-2.0.so.0")]
318
static extern void g_signal_stop_emission_by_name (IntPtr raw, string name);
324
void OnDragDataReceived (object sender, Gtk.DragDataReceivedArgs args)
326
bool stop_emission = false;
328
if (args.SelectionData.Length < 0)
331
if (args.Info == 1) {
332
foreach (Gdk.Atom atom in args.Context.Targets) {
333
if (atom.Name == "x-uid-list") {
334
// Parse MIME mails in tmp files
335
DropEmailUriList (args);
337
Gtk.Drag.GetData (Window.Editor,
339
Gdk.Atom.Intern ("x-uid-list", false),
342
Gdk.Drag.Status (args.Context,
345
stop_emission = true;
348
} else if (args.Info == 99) {
349
// Parse the evolution internal URLs and generate email:
350
// URLs we can pass on the commandline.
353
// Insert the email: links into the note, using the
354
// message subject as the display text.
355
InsertMailLinks (args.X, args.Y, xuid_list, subject_list);
357
Gtk.Drag.Finish (args.Context, true, false, args.Time);
358
stop_emission = true;
361
// No return value for drag_data_received so no way to stop the
362
// default TextView handler from running without resorting to
365
g_signal_stop_emission_by_name(Window.Editor.Handle,
366
"drag_data_received");
370
void DropEmailUriList (Gtk.DragDataReceivedArgs args)
372
string uri_string = Encoding.UTF8.GetString (args.SelectionData.Data);
374
subject_list = new ArrayList();
376
UriList uri_list = new UriList (uri_string);
377
foreach (Uri uri in uri_list) {
378
Logger.Log ("Evolution: Dropped URI: {0}", uri.LocalPath);
380
int mail_fd = Syscall.open (uri.LocalPath, OpenFlags.O_RDONLY);
384
GMime.Stream stream = new GMime.StreamFs (mail_fd);
385
GMime.Parser parser = new GMime.Parser (stream);
386
parser.ScanFrom = true;
388
// Use GMime to read the RFC822 message bodies (in temp
389
// files pointed to by a uri-list) in MBOX format, so we
390
// can get subject/sender/date info.
391
while (!parser.Eos()) {
392
GMime.Message message = parser.ConstructMessage ();
396
string subject = GMime.Utils.HeaderDecodePhrase (message.Subject);
397
subject_list.Add (subject);
400
Logger.Log ("Evolution: Message Subject: {0}", subject);
409
void DropXUidList (Gtk.DragDataReceivedArgs args)
411
// FIXME: x-uid-list is an Evolution-internal drop type.
412
// We shouldn't be using it, but there is no other way
413
// to get the info we need to be able to generate email:
414
// URIs evo will open on the command-line.
416
// x-uid-list Format: "uri\0uid1\0uid2\0uid3\0...\0uidn"
418
string source = Encoding.UTF8.GetString (args.SelectionData.Data);
419
string [] list = source.Split ('\0');
421
xuid_list = new ArrayList ();
423
Logger.Log ("Evolution: Dropped XUid: uri = '{0}'", list [0]);
425
for (int i = 1; i < list.Length; i++) {
426
if (list [i] == string.Empty)
430
EvoUtils.EmailUriFromDropUri (list [0] /* evo account uri */,
431
list [i] /* message uid */);
433
Logger.Log ("Evolution: Translating XUid uid='{0}' to uri='{1}'",
437
xuid_list.Add (launch_uri);
441
void InsertMailLinks (int x, int y, ArrayList xuid_list, ArrayList subject_list)
444
bool more_than_one = false;
446
// Place the cursor in the position where the uri was
447
// dropped, adjusting x,y by the TextView's VisibleRect.
448
Gdk.Rectangle rect = Window.Editor.VisibleRect;
451
Gtk.TextIter cursor = Window.Editor.GetIterAtLocation (x, y);
452
Buffer.PlaceCursor (cursor);
454
foreach (string subject in subject_list) {
458
cursor = Buffer.GetIterAtMark (Buffer.InsertMark);
460
if (cursor.LineOffset == 0)
461
Buffer.Insert (ref cursor, "\n");
463
Buffer.Insert (ref cursor, ", ");
466
string launch_uri = (string) xuid_list [message_idx++];
469
link_tag = (EmailLink) Note.TagTable.CreateDynamicTag ("link:evo-mail");
470
link_tag.EmailUri = launch_uri;
472
cursor = Buffer.GetIterAtMark (Buffer.InsertMark);
473
start_offset = cursor.Offset;
474
Buffer.Insert (ref cursor, subject);
476
Gtk.TextIter start = Buffer.GetIterAtOffset (start_offset);
477
Gtk.TextIter end = Buffer.GetIterAtMark (Buffer.InsertMark);
478
Buffer.ApplyTag (link_tag, start, end);
480
more_than_one = true;
b'\\ No newline at end of file'