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 namespace Beagle
.Daemon
.EvolutionMailDriver
{
38 [QueryableFlavor (Name
="EvolutionMail", Domain
=QueryDomain
.Local
, RequireInotify
=false)]
39 public class EvolutionMailQueryable
: LuceneQueryable
{
41 public int polling_interval_in_seconds
= 60;
43 private string local_path
, imap_path
, imap4_path
;
45 private MailCrawler crawler
;
47 private Hashtable generator_progress
= new Hashtable ();
50 // 1: Original version, stored all recipient addresses as a
52 // 2: Stores recipients in separate properties,
53 // filters/indexes all attachments
54 // 3: Make email addresses non-keyword, add sanitized version
55 // for searching for parts of an email address.
56 // 4: Make the flags property mutable, and create a property
57 // change Indexable when they change for IMAP generators.
58 // 5: No need for a separate sanitized version of email addresses.
59 private const int INDEX_VERSION
= 5;
61 public EvolutionMailQueryable () : base ("EvolutionMailIndex", INDEX_VERSION
)
63 this.local_path
= Path
.Combine (PathFinder
.HomeDir
, ".evolution/mail/local");
64 this.imap_path
= Path
.Combine (PathFinder
.HomeDir
, ".evolution/mail/imap");
65 this.imap4_path
= Path
.Combine (PathFinder
.HomeDir
, ".evolution/mail/imap4");
68 private void CrawlHook (Scheduler
.Task task
)
71 task
.Reschedule
= true;
72 task
.TriggerTime
= DateTime
.Now
.AddSeconds (polling_interval_in_seconds
);
75 //////////////////////////////////////////////////////////////////////////////////////////////
77 private void StartWorker ()
79 Logger
.Log
.Info ("Starting Evolution mail backend");
81 Stopwatch stopwatch
= new Stopwatch ();
84 // Check that we have data to index
85 if ((! Directory
.Exists (this.local_path
)) && (! Directory
.Exists (this.imap_path
))) {
86 // No mails present, repoll every minute
87 Logger
.Log
.Warn ("Evolution mail store not found, watching for it.");
88 GLib
.Timeout
.Add (60000, new GLib
.TimeoutHandler (CheckForMailData
));
92 Logger
.Log
.Debug ("Starting mail crawl");
93 State
= QueryableState
.Crawling
;
94 crawler
= new MailCrawler (this.local_path
, this.imap_path
, this.imap4_path
);
95 crawler
.MboxAddedEvent
+= IndexMbox
;
96 crawler
.SummaryAddedEvent
+= IndexSummary
;
98 State
= QueryableState
.Idle
;
99 Logger
.Log
.Debug ("Mail crawl finished");
101 // If we don't have inotify, we have to poll the file system. Ugh.
102 if (! Inotify
.Enabled
) {
103 Scheduler
.Task task
= Scheduler
.TaskFromHook (new Scheduler
.TaskHook (CrawlHook
));
104 task
.Tag
= "Crawling ~/.evolution to find summary changes";
106 ThisScheduler
.Add (task
);
110 Logger
.Log
.Info ("Evolution mail driver worker thread done in {0}",
114 public override void Start ()
118 ExceptionHandlingThread
.Start (new ThreadStart (StartWorker
));
121 private bool CheckForMailData ()
123 if ((! Directory
.Exists (this.local_path
)) && (! Directory
.Exists (this.imap_path
)))
124 return true; // continue polling
126 // Otherwise stop polling and start indexing
132 get { return "EvolutionMail"; }
135 public void IndexSummary (FileInfo summaryInfo
)
137 // If there's already a task running for this folder,
138 // don't interrupt it.
139 if (ThisScheduler
.ContainsByTag (summaryInfo
.FullName
)) {
140 Logger
.Log
.Debug ("Not adding task for already running task: {0}", summaryInfo
.FullName
);
144 Logger
.Log
.Debug ("Will index summary {0}", summaryInfo
.FullName
);
145 EvolutionMailIndexableGeneratorImap generator
= new EvolutionMailIndexableGeneratorImap (this, summaryInfo
);
147 task
= NewAddTask (generator
);
148 task
.Tag
= summaryInfo
.FullName
;
149 // IndexableGenerator tasks default to having priority Scheduler.Priority Generator
150 ThisScheduler
.Add (task
);
152 SetGeneratorProgress (generator
, 0);
155 public void IndexMbox (FileInfo mboxInfo
)
157 // If there's already a task running for this mbox,
158 // don't interrupt it.
159 if (ThisScheduler
.ContainsByTag (mboxInfo
.FullName
)) {
160 Logger
.Log
.Debug ("Not adding task for already running task: {0}", mboxInfo
.FullName
);
164 Logger
.Log
.Debug ("Will index mbox {0}", mboxInfo
.FullName
);
165 EvolutionMailIndexableGeneratorMbox generator
= new EvolutionMailIndexableGeneratorMbox (this, mboxInfo
);
167 task
= NewAddTask (generator
);
168 task
.Tag
= mboxInfo
.FullName
;
169 // IndexableGenerator tasks default to having priority Scheduler.Priority Generator
170 ThisScheduler
.Add (task
);
172 SetGeneratorProgress (generator
, 0);
175 public static Uri
EmailUri (string accountName
, string folderName
, string uid
)
177 return new Uri (String
.Format ("email://{0}/{1};uid={2}",
178 accountName
, folderName
, uid
));
181 // An embarrassingly unscientific attempt at getting progress
182 // information from the mail backend as a whole. Unfortunately
183 // the IMAP and mbox backends don't have a common unit of
184 // measurement (IMAP has number of messages, mbox number of
185 // bytes), so we can't get anything really accurate. We could
186 // try to normalize the byte count; that'd do us a little
188 public void SetGeneratorProgress (EvolutionMailIndexableGenerator generator
, int percent
)
190 this.generator_progress
[generator
] = percent
;
192 int i
= 0, total_percent
= 0;
193 foreach (int progress
in this.generator_progress
.Values
) {
194 total_percent
+= progress
;
198 Logger
.Log
.Debug ("Overall percent is {0}", (float) total_percent
/ i
);
200 this.ProgressPercent
= total_percent
/ i
;
203 public void RemoveGeneratorProgress (EvolutionMailIndexableGenerator generator
)
205 this.generator_progress
.Remove (generator
);
207 int i
= 0, total_percent
= 0;
208 foreach (int progress
in this.generator_progress
.Values
) {
209 total_percent
+= progress
;
213 Logger
.Log
.Debug ("Overall percent is {0}", (float) total_percent
/ i
);
215 this.ProgressPercent
= total_percent
/ i
;