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 private const int INDEX_VERSION
= 4;
60 public EvolutionMailQueryable () : base ("EvolutionMailIndex", INDEX_VERSION
)
62 this.local_path
= Path
.Combine (PathFinder
.HomeDir
, ".evolution/mail/local");
63 this.imap_path
= Path
.Combine (PathFinder
.HomeDir
, ".evolution/mail/imap");
64 this.imap4_path
= Path
.Combine (PathFinder
.HomeDir
, ".evolution/mail/imap4");
67 private void CrawlHook (Scheduler
.Task task
)
70 task
.Reschedule
= true;
71 task
.TriggerTime
= DateTime
.Now
.AddSeconds (polling_interval_in_seconds
);
74 //////////////////////////////////////////////////////////////////////////////////////////////
76 private void StartWorker ()
78 Logger
.Log
.Info ("Starting Evolution mail backend");
80 Stopwatch stopwatch
= new Stopwatch ();
83 // Check that we have data to index
84 if ((! Directory
.Exists (this.local_path
)) && (! Directory
.Exists (this.imap_path
))) {
85 // No mails present, repoll every minute
86 Logger
.Log
.Warn ("Evolution mail store not found, watching for it.");
87 GLib
.Timeout
.Add (60000, new GLib
.TimeoutHandler (CheckForMailData
));
91 Logger
.Log
.Debug ("Starting mail crawl");
92 State
= QueryableState
.Crawling
;
93 crawler
= new MailCrawler (this.local_path
, this.imap_path
, this.imap4_path
);
94 crawler
.MboxAddedEvent
+= IndexMbox
;
95 crawler
.SummaryAddedEvent
+= IndexSummary
;
97 State
= QueryableState
.Idle
;
98 Logger
.Log
.Debug ("Mail crawl finished");
100 // If we don't have inotify, we have to poll the file system. Ugh.
101 if (! Inotify
.Enabled
) {
102 Scheduler
.Task task
= Scheduler
.TaskFromHook (new Scheduler
.TaskHook (CrawlHook
));
103 task
.Tag
= "Crawling ~/.evolution to find summary changes";
105 ThisScheduler
.Add (task
);
109 Logger
.Log
.Info ("Evolution mail driver worker thread done in {0}",
113 public override void Start ()
117 ExceptionHandlingThread
.Start (new ThreadStart (StartWorker
));
120 private bool CheckForMailData ()
122 if ((! Directory
.Exists (this.local_path
)) && (! Directory
.Exists (this.imap_path
)))
123 return true; // continue polling
125 // Otherwise stop polling and start indexing
131 get { return "EvolutionMail"; }
134 public void IndexSummary (FileInfo summaryInfo
)
136 // If there's already a task running for this folder,
137 // don't interrupt it.
138 if (ThisScheduler
.ContainsByTag (summaryInfo
.FullName
)) {
139 Logger
.Log
.Debug ("Not adding task for already running task: {0}", summaryInfo
.FullName
);
143 Logger
.Log
.Debug ("Will index summary {0}", summaryInfo
.FullName
);
144 EvolutionMailIndexableGeneratorImap generator
= new EvolutionMailIndexableGeneratorImap (this, summaryInfo
);
146 task
= NewAddTask (generator
);
147 task
.Tag
= summaryInfo
.FullName
;
148 // IndexableGenerator tasks default to having priority Scheduler.Priority Generator
149 ThisScheduler
.Add (task
);
151 SetGeneratorProgress (generator
, 0);
154 public void IndexMbox (FileInfo mboxInfo
)
156 // If there's already a task running for this mbox,
157 // don't interrupt it.
158 if (ThisScheduler
.ContainsByTag (mboxInfo
.FullName
)) {
159 Logger
.Log
.Debug ("Not adding task for already running task: {0}", mboxInfo
.FullName
);
163 Logger
.Log
.Debug ("Will index mbox {0}", mboxInfo
.FullName
);
164 EvolutionMailIndexableGeneratorMbox generator
= new EvolutionMailIndexableGeneratorMbox (this, mboxInfo
);
166 task
= NewAddTask (generator
);
167 task
.Tag
= mboxInfo
.FullName
;
168 // IndexableGenerator tasks default to having priority Scheduler.Priority Generator
169 ThisScheduler
.Add (task
);
171 SetGeneratorProgress (generator
, 0);
174 public static Uri
EmailUri (string accountName
, string folderName
, string uid
)
176 return new Uri (String
.Format ("email://{0}/{1};uid={2}",
177 accountName
, folderName
, uid
));
180 // An embarrassingly unscientific attempt at getting progress
181 // information from the mail backend as a whole. Unfortunately
182 // the IMAP and mbox backends don't have a common unit of
183 // measurement (IMAP has number of messages, mbox number of
184 // bytes), so we can't get anything really accurate. We could
185 // try to normalize the byte count; that'd do us a little
187 public void SetGeneratorProgress (EvolutionMailIndexableGenerator generator
, int percent
)
189 this.generator_progress
[generator
] = percent
;
191 int i
= 0, total_percent
= 0;
192 foreach (int progress
in this.generator_progress
.Values
) {
193 total_percent
+= progress
;
197 Logger
.Log
.Debug ("Overall percent is {0}", (float) total_percent
/ i
);
199 this.ProgressPercent
= total_percent
/ i
;
202 public void RemoveGeneratorProgress (EvolutionMailIndexableGenerator generator
)
204 this.generator_progress
.Remove (generator
);
206 int i
= 0, total_percent
= 0;
207 foreach (int progress
in this.generator_progress
.Values
) {
208 total_percent
+= progress
;
212 Logger
.Log
.Debug ("Overall percent is {0}", (float) total_percent
/ i
);
214 this.ProgressPercent
= total_percent
/ i
;