From 23373506160b74dff2bf5929abf28db336b89ff8 Mon Sep 17 00:00:00 2001 From: joeshaw Date: Wed, 10 Aug 2005 18:54:35 +0000 Subject: [PATCH] New e-d-s backend which indexes all local addressbooks and calendars. Requires evo-sharp CVS right now (0.8) --- beagled/CalendarQueryable/CalendarQueryable.cs | 390 ------------------ beagled/EvolutionDataServerQueryable/EdsSource.cs | 104 +++++ .../EvolutionDataServerQueryable.cs | 443 ++++++++++++++++----- beagled/Makefile.am | 10 +- configure.in | 2 +- 5 files changed, 444 insertions(+), 505 deletions(-) delete mode 100644 beagled/CalendarQueryable/CalendarQueryable.cs create mode 100644 beagled/EvolutionDataServerQueryable/EdsSource.cs diff --git a/beagled/CalendarQueryable/CalendarQueryable.cs b/beagled/CalendarQueryable/CalendarQueryable.cs deleted file mode 100644 index f8e2cabb..00000000 --- a/beagled/CalendarQueryable/CalendarQueryable.cs +++ /dev/null @@ -1,390 +0,0 @@ -// -// CalendarQueryable.cs -// -// Copyright (C) 2004 Novell, Inc. -// - -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Collections; -using System.IO; -using System.Text; -using System.Threading; - -using Beagle.Daemon; -using Beagle.Util; - -using ICalParser = Semaview.Shared.ICalParser; - -namespace Beagle.Daemon.CalendarQueryable { - - [QueryableFlavor (Name="Calendar", Domain=QueryDomain.Local)] - public class CalendarQueryable : LuceneQueryable { - - public static Logger Log = Logger.Get ("calendar"); - private string cal_dir; - - Hashtable watched = new Hashtable (); - - public CalendarQueryable () : base ("CalendarIndex") - { - cal_dir = Path.Combine (PathFinder.HomeDir, ".evolution/calendar/local"); - } - - private void StartWorker () - { - Inotify.Event += OnInotifyEvent; - - Stopwatch timer = new Stopwatch (); - timer.Start (); - int foundCount = Watch (cal_dir); - timer.Stop (); - Log.Info ("Found {0} calendars in {1}", foundCount, timer); - } - - public override void Start () - { - base.Start (); - - ExceptionHandlingThread.Start (new ThreadStart (StartWorker)); - } - - private int Watch (string path) - { - DirectoryInfo root = new DirectoryInfo (path); - if (! root.Exists) - return 0; - - int file_count = 0; - - Queue queue = new Queue (); - queue.Enqueue (root); - - while (queue.Count > 0) { - DirectoryInfo dir = queue.Dequeue () as DirectoryInfo; - - int wd = Inotify.Watch (dir.FullName, - Inotify.EventType.CreateSubdir - | Inotify.EventType.Modify); - watched [wd] = true; - - foreach (FileInfo file in dir.GetFiles ()) { - IndexCalendar (file.FullName, Scheduler.Priority.Generator); - ++file_count; - } - - foreach (DirectoryInfo subdir in dir.GetDirectories ()) - queue.Enqueue (subdir); - } - - return file_count; - } - - private void OnInotifyEvent (int wd, - string path, - string subitem, - string srcpath, - Inotify.EventType type) - { - if (subitem == "" || ! watched.Contains (wd)) - return; - - string full_path = Path.Combine (path, subitem); - - Console.WriteLine ("{0}: {1}", type, full_path); - - switch (type) { - - case Inotify.EventType.CreateSubdir: - Watch (full_path); - break; - - case Inotify.EventType.Modify: - IndexCalendar (full_path, Scheduler.Priority.Immediate); - break; - } - } - - private void IndexCalendar (string filename, Scheduler.Priority priority) - { - FileInfo info = new FileInfo (filename); - if (! info.Exists || Driver.IsUpToDate (filename)) - return; - - Scheduler.TaskGroup group; - group = NewMarkingTaskGroup (filename, info.LastWriteTime); - - IndexableEmitter emitter = new IndexableEmitter (); - ICalParser.Parser parser = new ICalParser.Parser (new StreamReader (info.FullName), emitter); - parser.Parse (); - - if (!parser.HasErrors) { - foreach (Indexable indexable in emitter.Indexables) { - Scheduler.Task task = NewAddTask (indexable); - task.Priority = priority; - task.SubPriority = 0; - task.AddTaskGroup (group); - ThisScheduler.Add (task); - } - } - } - } - - class IndexableEmitter : ICalParser.IEmitter { - private ArrayList indexables = new ArrayList (); - private ICalParser.Parser parser; - - // Our current state - private Indexable cur = null; - private string cur_id = null; - - public ICollection Indexables { - get { return this.indexables; } - } - - private static DateTime ParseICalDate (string icaldate, bool utc) - { - // There is no error checking at all. - string year_str = icaldate.Substring (0, 4); - string month_str = icaldate.Substring (4, 2); - string day_str = icaldate.Substring (6, 2); - - DateTime date; - - if (icaldate.Length >= 15) { - string hour_str = icaldate.Substring (9, 2); - string minute_str = icaldate.Substring (11, 2); - string second_str = icaldate.Substring (13, 2); - - date = new DateTime (Convert.ToInt32 (year_str), - Convert.ToInt32 (month_str), - Convert.ToInt32 (day_str), - Convert.ToInt32 (hour_str), - Convert.ToInt32 (minute_str), - Convert.ToInt32 (second_str)); - - if (utc) { - TimeSpan utc_offset = DateTime.Now - DateTime.UtcNow; - - date += utc_offset; - } - } else { - date = new DateTime (Convert.ToInt32 (year_str), - Convert.ToInt32 (month_str), - Convert.ToInt32 (day_str)); - } - - return date; - } - - private static DateTime ParseICalDate (string icaldate) - { - bool utc = icaldate.EndsWith ("Z"); - - return ParseICalDate (icaldate, utc); - } - - // Implement IEmitter - public void doIntro () - { - CalendarQueryable.Log.Debug ("-------"); - } - - public void doOutro () - { - CalendarQueryable.Log.Debug ("-------"); - } - - public void doEnd (ICalParser.Token t) - { - CalendarQueryable.Log.Debug ("doEnd: {0}", t.TokenText); - - this.cur_id = null; - - if (t.TokenText.ToLower () == "vevent") { - this.indexables.Add (this.cur); - this.cur = null; - } - } - - public void doResourceBegin (ICalParser.Token t) - { - CalendarQueryable.Log.Debug ("doResourceBegin: {0}", t.TokenText); - } - - public void doBegin (ICalParser.Token t) - { - CalendarQueryable.Log.Debug ("doBegin: {0}", t.TokenText); - } - - public void doComponentBegin (ICalParser.Token t) - { - CalendarQueryable.Log.Debug ("doComponentBegin: {0}", t.TokenText); - - // FIXME: Need more types to index. - if (t.TokenText.ToLower () != "vevent") - return; - - this.cur = new Indexable (); - this.cur.Type = "Calendar"; - } - - public void doComponent () - { - } - - public void doEndComponent () - { - } - - public void doID (ICalParser.Token t) - { - CalendarQueryable.Log.Debug ("doID: {0}", t.TokenText); - - if (this.cur == null) - return; - - this.cur_id = t.TokenText; - } - - public void doSymbolic (ICalParser.Token t) - { - CalendarQueryable.Log.Debug ("doSymbolic: {0}", t.TokenText); - } - - public void doResource (ICalParser.Token t) - { - CalendarQueryable.Log.Debug ("doResource: {0}", t.TokenText); - } - - public void doURIResource (ICalParser.Token t) - { - CalendarQueryable.Log.Debug ("doURIResource: {0}", t.TokenText); - } - - public void doMailto (ICalParser.Token t) - { - CalendarQueryable.Log.Debug ("doMailto: {0}", t.TokenText); - } - - public void doValueProperty (ICalParser.Token t, ICalParser.Token iprop) - { - CalendarQueryable.Log.Debug ("doValueProperty: {0} {1}", t.TokenText, iprop == null ? "(null)" : iprop.TokenText); - - if (this.cur == null || this.cur_id == null) - return; - - switch (this.cur_id.ToLower ()) { - case "dtstart": - // When the event starts; in local timezone. - this.cur.AddProperty (Property.NewDate ("fixme:starttime", ParseICalDate (t.TokenText))); - break; - - case "dtend": - // When the event starts; in local timezone. - this.cur.AddProperty (Property.NewDate ("fixme:endtime", ParseICalDate (t.TokenText))); - break; - } - } - - public void doIprop (ICalParser.Token t, ICalParser.Token iprop) - { - CalendarQueryable.Log.Debug ("doIprop: {0} {1}", t.TokenText, iprop.TokenText); - } - - public void doRest (ICalParser.Token t, ICalParser.Token id) - { - CalendarQueryable.Log.Debug ("doRest: {0} {1}", t.TokenText, id.TokenText); - - if (this.cur == null || this.cur_id == null) - return; - - switch (this.cur_id.ToLower ()) { - case "uid": - this.cur.Uri = new Uri ("calendar:///" + t.TokenText); - break; - - case "dtstart": - // When the event starts; in local timezone. - // Usually this won't be processed here, it'll - // more likely be in doValueProperty w/ a - // timezone. - this.cur.AddProperty (Property.NewDate ("fixme:starttime", ParseICalDate (t.TokenText))); - break; - - case "dtend": - // When the event ends; in local timezone. - // Same deal as dtstart above. - this.cur.AddProperty (Property.NewDate ("fixme:endtime", ParseICalDate (t.TokenText))); - break; - - case "lastmodified": - // Always in GMT - this.cur.Timestamp = ParseICalDate (t.TokenText, true); - break; - - case "summary": - // Short summary of the event - this.cur.AddProperty (Property.New ("fixme:summary", t.TokenText)); - break; - - case "description": - // Longer description of the event - StringReader reader = new StringReader (t.TokenText); - this.cur.SetTextReader (reader); - break; - - case "location": - // Where the event takes place - this.cur.AddProperty (Property.NewKeyword ("fixme:location", t.TokenText)); - break; - - case "categories": - // Categories associated with this event - this.cur.AddProperty (Property.NewKeyword ("fixme:categories", t.TokenText)); - break; - - case "class": - // private, public, or confidential - this.cur.AddProperty (Property.NewKeyword ("fixme:class", t.TokenText)); - break; - } - - this.cur_id = null; - } - - public void doAttribute (ICalParser.Token t1, ICalParser.Token t2) - { - CalendarQueryable.Log.Debug ("doAttribute: {0} {1}", t1.TokenText, t2.TokenText); - } - - public ICalParser.Parser VParser { - get { return this.parser; } - set { this.parser = value; } - } - - public void emit (string val) - { - CalendarQueryable.Log.Debug ("emit: {0}", val); - } - } -} diff --git a/beagled/EvolutionDataServerQueryable/EdsSource.cs b/beagled/EvolutionDataServerQueryable/EdsSource.cs new file mode 100644 index 00000000..56b9f303 --- /dev/null +++ b/beagled/EvolutionDataServerQueryable/EdsSource.cs @@ -0,0 +1,104 @@ +// +// EdsSource.cs +// +// Copyright (C) 2005 Novell, Inc. +// + +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System; + +using Beagle.Util; + +using Evolution; + +namespace Beagle.Daemon.EvolutionDataServerQueryable { + + public delegate void IndexSourceHandler (Evolution.Source src); + + public class EdsSource { + + SourceList source_list; + + public IndexSourceHandler IndexSourceAll; + public IndexSourceHandler IndexSourceChanges; + public IndexSourceHandler RemoveSource; + + public EdsSource (string gconf_key) + { + this.source_list = new SourceList (gconf_key); + + if (this.source_list == null) { + // FIXME: We may want to watch for the creation + // of the sources GConf key + Logger.Log.Info ("No sources found at {0}", gconf_key); + return; + } + + this.source_list.GroupAdded += OnGroupAdded; + this.source_list.GroupRemoved += OnGroupRemoved; + } + + public void Index () + { + if (this.source_list == null) + return; + + foreach (SourceGroup group in this.source_list.Groups) + IndexSourceGroup (group); + } + + private void IndexSourceGroup (SourceGroup group) + { + group.SourceAdded += OnSourceAdded; + group.SourceRemoved += OnSourceRemoved; + + foreach (Evolution.Source src in group.Sources) + this.IndexSourceChanges (src); + } + + private void RemoveSourceGroup (SourceGroup group) + { + foreach (Evolution.Source src in group.Sources) + this.RemoveSource (src); + } + + private void OnGroupAdded (object o, GroupAddedArgs args) + { + IndexSourceGroup (args.Group); + } + + private void OnGroupRemoved (object o, GroupRemovedArgs args) + { + RemoveSourceGroup (args.Group); + } + + private void OnSourceAdded (object o, SourceAddedArgs args) + { + this.IndexSourceAll (args.Source); + } + + private void OnSourceRemoved (object o, SourceRemovedArgs args) + { + this.RemoveSource (args.Source); + } + } +} \ No newline at end of file diff --git a/beagled/EvolutionDataServerQueryable/EvolutionDataServerQueryable.cs b/beagled/EvolutionDataServerQueryable/EvolutionDataServerQueryable.cs index 999267ad..dc87a43c 100644 --- a/beagled/EvolutionDataServerQueryable/EvolutionDataServerQueryable.cs +++ b/beagled/EvolutionDataServerQueryable/EvolutionDataServerQueryable.cs @@ -35,29 +35,22 @@ using System.IO; using Beagle.Daemon; using Beagle.Util; -using Lucene.Net.Index; -using Lucene.Net.Store; -using Lucene.Net.Documents; -using Lucene.Net.QueryParsers; -using LNS = Lucene.Net.Search; +using Evolution; namespace Beagle.Daemon.EvolutionDataServerQueryable { [QueryableFlavor (Name="EvolutionDataServer", Domain=QueryDomain.Local, RequireInotify=false)] public class EvolutionDataServerQueryable : LuceneQueryable { - private static Logger log = Logger.Get ("addressbook"); //private Scheduler.Priority priority = Scheduler.Priority.Immediate; private Scheduler.Priority priority = Scheduler.Priority.Delayed; private string photo_dir; private DateTime sequence_start_time; - private DateTime indexed_through = DateTime.MinValue; + private DateTime addressbook_indexed_through = DateTime.MinValue; + private DateTime calendar_indexed_through = DateTime.MinValue; - private Evolution.Book addressbook = null; - private Evolution.BookView book_view; - - public EvolutionDataServerQueryable () : base ("AddressbookIndex") + public EvolutionDataServerQueryable () : base ("EvolutionDataServerIndex") { photo_dir = Path.Combine (Driver.IndexDirectory, "Photos"); System.IO.Directory.CreateDirectory (photo_dir); @@ -67,36 +60,130 @@ namespace Beagle.Daemon.EvolutionDataServerQueryable { { base.Start (); - try { - addressbook = Evolution.Book.NewSystemAddressbook (); - addressbook.Open (true); - - Evolution.BookQuery q = Evolution.BookQuery.AnyFieldContains (""); - ArrayList dummy = new ArrayList (); - book_view = addressbook.GetBookView (q, - dummy, - -1); - - book_view.ContactsAdded += OnContactsAdded; - book_view.ContactsRemoved += OnContactsRemoved; - book_view.ContactsChanged += OnContactsChanged; - book_view.SequenceComplete += OnSequenceComplete; - - sequence_start_time = DateTime.Now; - book_view.Start (); + Logger.Log.Info ("Scanning addressbooks and calendars"); + Stopwatch timer = new Stopwatch (); + timer.Start (); + + EdsSource src; + + src = new EdsSource ("/apps/evolution/addressbook/sources"); + src.IndexSourceAll += AddressbookIndexSourceAll; + src.IndexSourceChanges += AddressbookIndexSourceChanges; + src.RemoveSource += AddressbookRemoveSource; + src.Index (); + + src = new EdsSource ("/apps/evolution/calendar/sources"); + src.IndexSourceAll += CalendarIndexSourceAll; + src.IndexSourceChanges += CalendarIndexSourceChanges; + src.RemoveSource += CalendarRemoveSource; + src.Index (); + + timer.Stop (); + Logger.Log.Info ("Scanned addressbooks and calendars in {0}", timer); + } + + public void Add (Indexable indexable, Scheduler.Priority priority) + { + Scheduler.Task task; + task = NewAddTask (indexable); + task.Priority = priority; + ThisScheduler.Add (task); + } - } catch (Exception ex) { - addressbook = null; - log.Warn ("Could not open Evolution addressbook. Addressbook searching is disabled."); - log.Debug (ex); + public void Remove (Uri uri) + { + Scheduler.Task task; + task = NewRemoveTask (uri); + task.Priority = Scheduler.Priority.Immediate; + ThisScheduler.Add (task); + } + + /////////////////////////////////////// + + private void AddressbookIndexSourceAll (Evolution.Source src) + { + if (!src.IsLocal ()) { + Logger.Log.Debug ("Skipping remote addressbook {0}", src.Uri); + return; } + + Logger.Log.Debug ("Indexing all data in this addressbook ({0})!", src.Uri); + + Book book = new Book (src); + book.Open (true); + + BookView book_view = book.GetBookView (BookQuery.AnyFieldContains (""), + new object [0], + -1); + + book_view.ContactsAdded += OnContactsAdded; + book_view.ContactsRemoved += OnContactsRemoved; + book_view.ContactsChanged += OnContactsChanged; + book_view.SequenceComplete += OnSequenceComplete; + + book_view.Start (); } - private DateTime IndexedThrough { + private void AddressbookIndexSourceChanges (Evolution.Source src) + { + if (!src.IsLocal ()) { + Logger.Log.Debug ("Skipping remote addressbook {0}", src.Uri); + return; + } + + Book book = new Book (src); + book.Open (true); + + Contact[] added, changed; + string[] removed; + + Logger.Log.Debug ("Getting addressbook changes for {0}", src.Uri); + book.GetChanges ("beagle-" + Driver.Fingerprint, out added, out changed, out removed); + Logger.Log.Debug ("Addressbook {0}: {1} added, {2} changed, {3} removed", + book.Uri, added.Length, changed.Length, removed.Length); + + foreach (Contact contact in added) + AddContact (contact); + + foreach (Contact contact in changed) + AddContact (contact); + + foreach (string id in removed) + RemoveContact (id); + + BookView book_view = book.GetBookView (BookQuery.AnyFieldContains (""), + new object [0], + -1); + + book_view.ContactsAdded += OnContactsAdded; + book_view.ContactsRemoved += OnContactsRemoved; + book_view.ContactsChanged += OnContactsChanged; + book_view.SequenceComplete += OnSequenceComplete; + + book_view.Start (); + } + + private void AddressbookRemoveSource (Evolution.Source src) + { + // FIXME: We need to index the group's UID and then + // we need a way to schedule removal tasks for + // anything that matches that lucene property + Logger.Log.Debug ("FIXME: Remove addressbook source {0}", src.Uri); + } + + private static Uri GetContactUri (Evolution.Contact contact) { + return GetContactUri (contact.Id); + } + + private static Uri GetContactUri (string id) { + return new Uri ("contact://" + id, true); // FIXME! + } + + private DateTime AddressbookIndexedThrough { get { - if (indexed_through == DateTime.MinValue) { - string filename = Path.Combine (IndexStoreDirectory, "IndexedThrough"); + if (addressbook_indexed_through == DateTime.MinValue) { + string filename = Path.Combine (IndexStoreDirectory, "AddressbookIndexedThrough"); string line = null; try { @@ -106,54 +193,20 @@ namespace Beagle.Daemon.EvolutionDataServerQueryable { } catch (Exception ex) { } if (line != null) - indexed_through = StringFu.StringToDateTime (line); + addressbook_indexed_through = StringFu.StringToDateTime (line); } - return indexed_through; + return addressbook_indexed_through; } set { - indexed_through = value; + addressbook_indexed_through = value; - string filename = Path.Combine (IndexStoreDirectory, "IndexedThrough"); + string filename = Path.Combine (IndexStoreDirectory, "AddressbookIndexedThrough"); StreamWriter sw = new StreamWriter (filename); - sw.WriteLine (StringFu.DateTimeToString (indexed_through)); + sw.WriteLine (StringFu.DateTimeToString (addressbook_indexed_through)); sw.Close (); } } - - private Uri GetContactUri (Evolution.Contact contact) { - return GetContactUri (contact.Id); - } - - private Uri GetContactUri (string id) { - return new Uri ("contact://" + id, true); // FIXME! - } - - private static string ExtractFieldFromVCardString (string vcard_str, string field) - { - field = "\n" + field + ":"; - - int i = vcard_str.IndexOf (field); - if (i == -1) - return null; - i += field.Length; - - int j = vcard_str.IndexOf ('\n', i); - - string retval = null; - if (j == -1) - retval = vcard_str.Substring (i); - else - retval = vcard_str.Substring (i, j-i); - - if (retval != null) { - retval = retval.Trim (); - if (retval.Length == 0) - retval = null; - } - - return retval; - } private static DateTime RevStringToDateTime (string date_str) { @@ -177,12 +230,9 @@ namespace Beagle.Daemon.EvolutionDataServerQueryable { private Indexable ContactToIndexable (Evolution.Contact contact) { - string vcard_str = contact.ToString (Evolution.VCardFormat.Three0); - - string rev_str = ExtractFieldFromVCardString (vcard_str, "REV"); - DateTime rev = RevStringToDateTime (rev_str); + DateTime rev = RevStringToDateTime (contact.Rev); - if (rev != DateTime.MinValue && rev < IndexedThrough) + if (rev != DateTime.MinValue && rev < AddressbookIndexedThrough) return null; Indexable indexable = new Indexable (GetContactUri (contact)); @@ -277,44 +327,34 @@ namespace Beagle.Daemon.EvolutionDataServerQueryable { return indexable; } - private void AddContacts (IEnumerable contacts) + private void AddContact (Contact contact) { - foreach (Evolution.Contact contact in contacts) { - Indexable indexable = ContactToIndexable (contact); - if (indexable != null) { - Scheduler.Task task; - task = NewAddTask (indexable); - task.Priority = priority; - ThisScheduler.Add (task); - } - } + Indexable indexable = ContactToIndexable (contact); + if (indexable != null) + Add (indexable, priority); } - - private void RemoveContacts (IEnumerable contacts) - { - foreach (string id in contacts) { - Scheduler.Task task; - task = NewRemoveTask (GetContactUri (id)); - task.Priority = priority; - ThisScheduler.Add (task); + private void RemoveContact (string id) + { + Remove (GetContactUri (id)); - string filename = GetPhotoFilename (id); - if (filename != null && File.Exists (filename)) - File.Delete (filename); - } + string filename = GetPhotoFilename (id); + if (filename != null && File.Exists (filename)) + File.Delete (filename); } private void OnContactsAdded (object o, Evolution.ContactsAddedArgs args) { - AddContacts (args.Contacts); + foreach (Evolution.Contact contact in args.Contacts) + AddContact (contact); } private void OnContactsChanged (object o, Evolution.ContactsChangedArgs args) { - AddContacts (args.Contacts); + foreach (Evolution.Contact contact in args.Contacts) + AddContact (contact); } private void OnContactsRemoved (object o, @@ -328,8 +368,9 @@ namespace Beagle.Daemon.EvolutionDataServerQueryable { GLib.List id_list = new GLib.List (args.Ids.Handle, typeof (string)); - - RemoveContacts (id_list); + + foreach (string id in id_list) + RemoveContact (id); } private string GetPhotoFilename (string id) @@ -344,7 +385,7 @@ namespace Beagle.Daemon.EvolutionDataServerQueryable { // running will be re-indexed during the next scan. // That isn't optimal, but is much better than the // current situation. - IndexedThrough = sequence_start_time; + AddressbookIndexedThrough = sequence_start_time; // Now that we're done synching with the original // state of the addressbook, switch all new changes to @@ -352,6 +393,194 @@ namespace Beagle.Daemon.EvolutionDataServerQueryable { priority = Scheduler.Priority.Immediate; } - } + /////////////////////////////////////// + + private void CalendarIndexSourceAll (Evolution.Source src) + { + if (!src.IsLocal ()) { + Logger.Log.Debug ("Skipping remote calendar {0}", src.Uri); + return; + } + + Logger.Log.Debug ("Indexing all data in this calendar ({0})!", src.Uri); + + Cal cal = new Cal (src, CalSourceType.Event); + cal.Open (true); + + CalComponent[] event_list = cal.GetItems ("#t"); + Logger.Log.Debug ("Calendar has {0} items", event_list.Length); + + foreach (CalComponent cc in event_list) + IndexCalComponent (cc, Scheduler.Priority.Immediate); + + CalView cal_view = cal.GetCalView ("#t"); + cal_view.ObjectsAdded += OnObjectsAdded; + cal_view.ObjectsModified += OnObjectsModified; + cal_view.ObjectsRemoved += OnObjectsRemoved; + cal_view.Start (); + } + + private void CalendarIndexSourceChanges (Evolution.Source src) + { + if (!src.IsLocal ()) { + Logger.Log.Debug ("Skipping remote calendar {0}", src.Uri); + return; + } + + Cal cal = new Cal (src, CalSourceType.Event); + cal.Open (true); + + CalComponent[] new_items, update_items; + string[] remove_items; + + Logger.Log.Debug ("Getting calendar changes for {0}", src.Uri); + cal.GetChanges ("beagle-" + this.Driver.Fingerprint, out new_items, out update_items, out remove_items); + Logger.Log.Debug ("Calendar {0}: {1} new items, {2} updated items, {3} removed items", + cal.Uri, new_items.Length, update_items.Length, remove_items.Length); + + foreach (CalComponent cc in new_items) + IndexCalComponent (cc, Scheduler.Priority.Immediate); + + foreach (CalComponent cc in update_items) + IndexCalComponent (cc, Scheduler.Priority.Immediate); + + foreach (string id in remove_items) { + // FIXME: Broken in evo-sharp right now + //RemoveCalComponent (id); + } + + CalView cal_view = cal.GetCalView ("#t"); + cal_view.ObjectsAdded += OnObjectsAdded; + cal_view.ObjectsModified += OnObjectsModified; + cal_view.ObjectsRemoved += OnObjectsRemoved; + cal_view.Start (); + } + + private void CalendarRemoveSource (Evolution.Source src) + { + // FIXME: We need to index the group's UID and then + // we need a way to schedule removal tasks for + // anything that matches that lucene property + Logger.Log.Debug ("FIXME: Remove calendar source {0}", src.Uri); + } + + private static Uri GetCalendarUri (CalComponent cc) { + return GetContactUri (cc.Uid); + } + + private static Uri GetCalendarUri (string id) { + return new Uri ("calendar://" + id, true); // FIXME! + } + + private DateTime CalendarIndexedThrough { + + get { + if (calendar_indexed_through == DateTime.MinValue) { + string filename = Path.Combine (IndexStoreDirectory, "CalendarIndexedThrough"); + + string line = null; + try { + StreamReader sr = new StreamReader (filename); + line = sr.ReadLine (); + sr.Close (); + } catch (Exception ex) { } + + if (line != null) + calendar_indexed_through = StringFu.StringToDateTime (line); + } + return calendar_indexed_through; + } + + set { + calendar_indexed_through = value; + + string filename = Path.Combine (IndexStoreDirectory, "CalendarIndexedThrough"); + StreamWriter sw = new StreamWriter (filename); + sw.WriteLine (StringFu.DateTimeToString (calendar_indexed_through)); + sw.Close (); + } + } + + private void IndexCalComponent (CalComponent cc, Scheduler.Priority priority) + { + Indexable indexable = CalComponentToIndexable (cc); + Add (indexable, priority); + } + + private void RemoveCalComponent (string id) + { + Remove (GetCalendarUri (id)); + } + + private Indexable CalComponentToIndexable (CalComponent cc) + { + Indexable indexable = new Indexable (new Uri ("calendar:///" + cc.Uid)); + + indexable.Timestamp = cc.Dtstart; + indexable.Type = "Calendar"; + + indexable.AddProperty (Property.NewKeyword ("fixme:uid", cc.Uid)); + indexable.AddProperty (Property.NewDate ("fixme:starttime", cc.Dtstart)); + indexable.AddProperty (Property.NewDate ("fixme:endtime", cc.Dtend)); + + foreach (string attendee in cc.Attendees) + indexable.AddProperty (Property.New ("fixme:attendee", attendee)); + + foreach (string comment in cc.Comments) + indexable.AddProperty (Property.New ("fixme:comment", comment)); + + foreach (string description in cc.Descriptions) + indexable.AddProperty (Property.New ("fixme:description", description)); + + foreach (string summary in cc.Summaries) + indexable.AddProperty (Property.New ("fixme:summary", summary)); + + foreach (string category in cc.Categories) + indexable.AddProperty (Property.NewKeyword ("fixme:category", category)); + + foreach (string location in cc.Location) + indexable.AddProperty (Property.New ("fixme:location", location)); + + return indexable; + } + + private void OnObjectsAdded (object o, ObjectsAddedArgs args) + { + CalView cal_view = (CalView) o; + + foreach (CalComponent cc in CalUtil.CalCompFromICal (args.Objects.Handle, cal_view.Client)) { + // If the minimum date is unset/invalid, + // index it. + if (cc.LastModified <= CalUtil.MinDate || cc.LastModified > CalendarIndexedThrough) + IndexCalComponent (cc, Scheduler.Priority.Immediate); + } + } + + private void OnObjectsModified (object o, ObjectsModifiedArgs args) + { + CalView cal_view = (CalView) o; + + foreach (CalComponent cc in CalUtil.CalCompFromICal (args.Objects.Handle, cal_view.Client)) + IndexCalComponent (cc, Scheduler.Priority.Immediate); + } + + private void OnObjectsRemoved (object o, ObjectsRemovedArgs args) + { + // FIXME: This is a temporary workaround for the + // fact that the evolution bindings return a + // GLib.List with an object type, but there + // are really strings in there + + GLib.List id_list = new GLib.List (args.Uids.Handle, + typeof (string)); + + foreach (string uid in id_list) { + Scheduler.Task task; + task = NewRemoveTask (new Uri ("calendar:///" + uid)); + task.Priority = Scheduler.Priority.Immediate; + ThisScheduler.Add (task); + } + } + } } diff --git a/beagled/Makefile.am b/beagled/Makefile.am index c654dc86..7e15f6d5 100644 --- a/beagled/Makefile.am +++ b/beagled/Makefile.am @@ -254,7 +254,8 @@ GAIM_LOG_QUERYABLE_CSFILES = \ edsqueryable = $(srcdir)/EvolutionDataServerQueryable EDS_QUERYABLE_CSFILES = \ - $(edsqueryable)/EvolutionDataServerQueryable.cs + $(edsqueryable)/EvolutionDataServerQueryable.cs \ + $(edsqueryable)/EdsSource.cs tomboyqueryable = $(srcdir)/TomboyQueryable TOMBOY_QUERYABLE_CSFILES = \ @@ -287,11 +288,6 @@ indexingservicequeryable = $(srcdir)/IndexingServiceQueryable INDEXING_SERVICE_QUERYABLE_CSFILES = \ $(indexingservicequeryable)/IndexingServiceQueryable.cs -#calendarqueryable = $(srcdir)/CalendarQueryable -#CALENDAR_QUERYABLE_CSFILES = \ -# $(calendarqueryable)/CalendarQueryable.cs \ -# $(calendarqueryable)/ICalParser/*.cs - DAEMON_DLL_CSFILES = \ $(LUCENE_CSFILES) \ $(FILE_SYSTEM_QUERYABLE_CSFILES) \ @@ -361,7 +357,7 @@ endif if ENABLE_EVO_SHARP DAEMON_DLL_CSFLAGS += -define:ENABLE_EVO_SHARP -DAEMON_DLL_CSFILES += $(EDS_QUERYABLE_CSFILES) +DAEMON_DLL_CSFILES += $(EDS_QUERYABLE_CSFILES) DAEMON_DLL_ASSEMBLIES += $(EVO_SHARP_LIBS) endif diff --git a/configure.in b/configure.in index 46e85434..101653c7 100644 --- a/configure.in +++ b/configure.in @@ -285,7 +285,7 @@ AC_ARG_ENABLE([evolution-sharp], enable_evo_sharp=$enableval, enable_evo_sharp=auto) -EVOLUTION_SHARP_REQUIRED=0.6 +EVOLUTION_SHARP_REQUIRED=0.8 AC_SUBST(EVO_SHARP_LIBS) EVO_SHARP_PREFIX=`$PKG_CONFIG --variable=prefix evolution-sharp` -- 2.11.4.GIT