2 // EvolutionMailDriver.cs
4 // Copyright (C) 2004 Novell, Inc.
8 // Permission is hereby granted, free of charge, to any person obtaining a
9 // copy of this software and associated documentation files (the "Software"),
10 // to deal in the Software without restriction, including without limitation
11 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 // and/or sell copies of the Software, and to permit persons to whom the
13 // Software is furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 // DEALINGS IN THE SOFTWARE.
29 using System
.Collections
;
31 using System
.Threading
;
34 using Camel
= Beagle
.Util
.Camel
;
36 using LNI
= Lucene
.Net
.Index
;
38 namespace Beagle
.Daemon
.EvolutionMailDriver
{
40 [QueryableFlavor (Name
="EvolutionMail", Domain
=QueryDomain
.Local
, RequireInotify
=false)]
41 public class EvolutionMailQueryable
: LuceneQueryable
{
43 public int polling_interval_in_seconds
= 60;
45 private string local_path
, imap_path
, imap4_path
;
47 private MailCrawler crawler
;
49 private Hashtable generator_progress
= new Hashtable ();
52 // 1: Original version, stored all recipient addresses as a
54 // 2: Stores recipients in separate properties,
55 // filters/indexes all attachments
56 // 3: Make email addresses non-keyword, add sanitized version
57 // for searching for parts of an email address.
58 // 4: Make the flags property mutable, and create a property
59 // change Indexable when they change for IMAP generators.
60 private const int INDEX_VERSION
= 4;
62 public EvolutionMailQueryable () : base ("EvolutionMailIndex", INDEX_VERSION
)
64 this.local_path
= Path
.Combine (PathFinder
.HomeDir
, ".evolution/mail/local");
65 this.imap_path
= Path
.Combine (PathFinder
.HomeDir
, ".evolution/mail/imap");
66 this.imap4_path
= Path
.Combine (PathFinder
.HomeDir
, ".evolution/mail/imap4");
69 private void CrawlHook (Scheduler
.Task task
)
72 task
.Reschedule
= true;
73 task
.TriggerTime
= DateTime
.Now
.AddSeconds (polling_interval_in_seconds
);
76 //////////////////////////////////////////////////////////////////////////////////////////////
78 private void StartWorker ()
80 Logger
.Log
.Info ("Starting Evolution mail backend");
82 Stopwatch stopwatch
= new Stopwatch ();
85 // Check that we have data to index
86 if ((! Directory
.Exists (this.local_path
)) && (! Directory
.Exists (this.imap_path
))) {
87 // No mails present, repoll every minute
88 Logger
.Log
.Warn ("Evolution mail store not found, watching for it.");
89 GLib
.Timeout
.Add (60000, new GLib
.TimeoutHandler (CheckForMailData
));
93 Logger
.Log
.Debug ("Starting mail crawl");
94 State
= QueryableState
.Crawling
;
95 crawler
= new MailCrawler (this.local_path
, this.imap_path
, this.imap4_path
);
96 crawler
.MboxAddedEvent
+= IndexMbox
;
97 crawler
.SummaryAddedEvent
+= IndexSummary
;
99 State
= QueryableState
.Idle
;
100 Logger
.Log
.Debug ("Mail crawl finished");
102 // If we don't have inotify, we have to poll the file system. Ugh.
103 if (! Inotify
.Enabled
) {
104 Scheduler
.Task task
= Scheduler
.TaskFromHook (new Scheduler
.TaskHook (CrawlHook
));
105 task
.Tag
= "Crawling ~/.evolution to find summary changes";
107 ThisScheduler
.Add (task
);
111 Logger
.Log
.Info ("Evolution mail driver worker thread done in {0}",
115 public override void Start ()
119 ExceptionHandlingThread
.Start (new ThreadStart (StartWorker
));
122 private bool CheckForMailData ()
124 if ((! Directory
.Exists (this.local_path
)) && (! Directory
.Exists (this.imap_path
)))
125 return true; // continue polling
127 // Otherwise stop polling and start indexing
133 get { return "EvolutionMail"; }
136 public void IndexSummary (FileInfo summaryInfo
)
138 // If there's already a task running for this folder,
139 // don't interrupt it.
140 if (ThisScheduler
.ContainsByTag (summaryInfo
.FullName
)) {
141 Logger
.Log
.Debug ("Not adding task for already running task: {0}", summaryInfo
.FullName
);
145 Logger
.Log
.Debug ("Will index summary {0}", summaryInfo
.FullName
);
146 EvolutionMailIndexableGeneratorImap generator
= new EvolutionMailIndexableGeneratorImap (this, summaryInfo
);
148 task
= NewAddTask (generator
);
149 task
.Tag
= summaryInfo
.FullName
;
150 // IndexableGenerator tasks default to having priority Scheduler.Priority Generator
151 ThisScheduler
.Add (task
);
153 SetGeneratorProgress (generator
, 0);
156 public void IndexMbox (FileInfo mboxInfo
)
158 // If there's already a task running for this mbox,
159 // don't interrupt it.
160 if (ThisScheduler
.ContainsByTag (mboxInfo
.FullName
)) {
161 Logger
.Log
.Debug ("Not adding task for already running task: {0}", mboxInfo
.FullName
);
165 Logger
.Log
.Debug ("Will index mbox {0}", mboxInfo
.FullName
);
166 EvolutionMailIndexableGeneratorMbox generator
= new EvolutionMailIndexableGeneratorMbox (this, mboxInfo
);
168 task
= NewAddTask (generator
);
169 task
.Tag
= mboxInfo
.FullName
;
170 // IndexableGenerator tasks default to having priority Scheduler.Priority Generator
171 ThisScheduler
.Add (task
);
173 SetGeneratorProgress (generator
, 0);
176 public static Uri
EmailUri (string accountName
, string folderName
, string uid
)
178 return new Uri (String
.Format ("email://{0}/{1};uid={2}",
179 accountName
, folderName
, uid
));
182 // An embarrassingly unscientific attempt at getting progress
183 // information from the mail backend as a whole. Unfortunately
184 // the IMAP and mbox backends don't have a common unit of
185 // measurement (IMAP has number of messages, mbox number of
186 // bytes), so we can't get anything really accurate. We could
187 // try to normalize the byte count; that'd do us a little
189 public void SetGeneratorProgress (EvolutionMailIndexableGenerator generator
, int percent
)
191 this.generator_progress
[generator
] = percent
;
193 int i
= 0, total_percent
= 0;
194 foreach (int progress
in this.generator_progress
.Values
) {
195 total_percent
+= progress
;
199 Logger
.Log
.Debug ("Overall percent is {0}", (float) total_percent
/ i
);
201 this.ProgressPercent
= total_percent
/ i
;
204 public void RemoveGeneratorProgress (EvolutionMailIndexableGenerator generator
)
206 this.generator_progress
.Remove (generator
);
208 int i
= 0, total_percent
= 0;
209 foreach (int progress
in this.generator_progress
.Values
) {
210 total_percent
+= progress
;
214 Logger
.Log
.Debug ("Overall percent is {0}", (float) total_percent
/ i
);
216 this.ProgressPercent
= total_percent
/ i
;