Remove debug statements from KCal backends.
[beagle.git] / beagled / KMailQueryable / KMailQueryable.cs
blob0ba38c3c0c43841abec231d54edee66f50735a40
1 //
2 // KMailQueryable.cs
3 //
4 // Copyright (C) 2005 Novell, Inc.
5 // Copyright (C) 2005 Debajyoti Bera
6 //
7 //
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.
28 using System;
29 using System.Collections;
30 using System.IO;
31 using System.Threading;
33 using Beagle.Util;
35 namespace Beagle.Daemon.KMailQueryable {
37 [QueryableFlavor (Name="KMail", Domain=QueryDomain.Local, RequireInotify=false)]
38 public class KMailQueryable : LuceneFileQueryable {
40 // for non-inotify case, poll after this number of seconds
41 public const int polling_interval_in_seconds = 300;
42 // mail folder paths
43 private string local_path, dimap_path;
44 // indexers - one for each mailfolder path
45 private KMailIndexer local_indexer, dimap_indexer;
46 // global variable
47 public static bool gmime_initialized = false;
48 public static void InitializeGMime ()
50 if (!gmime_initialized) {
51 GMime.Global.Init ();
52 gmime_initialized = true;
56 // name of the sentmail folder - should be parsed from kmailrc
57 private string sentmail_foldername;
58 public string SentMailFolderName {
59 get { return sentmail_foldername; }
62 public KMailQueryable () : base ("KMailIndex")
64 // the local mail path is different for different distributions
65 local_path = GuessLocalFolderPath ();
66 if (local_path == null) {
67 Logger.Log.Info ("KMail folders not found. Will keep trying ");
68 } else
69 Logger.Log.Info ("Guessing for location of KMail folders ... found at " + local_path);
70 // I hope there is no ambiguity over imap path :P
71 dimap_path = Path.Combine (PathFinder.HomeDir, ".kde");
72 dimap_path = Path.Combine (dimap_path, "share");
73 dimap_path = Path.Combine (dimap_path, "apps");
74 dimap_path = Path.Combine (dimap_path, "kmail");
75 dimap_path = Path.Combine (dimap_path, "dimap");
77 local_indexer = null;
78 dimap_indexer = null;
79 sentmail_foldername = "sent-mail";
82 //////////////////////////////////////////////////////////////////////////////////////////////
84 /**
85 * initial method called by the daemon
87 public override void Start ()
89 base.Start ();
90 ExceptionHandlingThread.Start (new ThreadStart (StartWorker));
93 /**
94 * for non-inotify case, this method is invoked repeatedly
96 private void CrawlHook (Scheduler.Task task)
98 if (local_indexer != null)
99 local_indexer.Crawl ();
100 if (dimap_indexer != null)
101 dimap_indexer.Crawl ();
102 task.Reschedule = true;
103 task.TriggerTime = DateTime.Now.AddSeconds (polling_interval_in_seconds);
107 * called by Start(), starts actual work
108 * create indexers
109 * ask indexers to crawl the mails
110 * for non-inotify case, ask to poll
112 private void StartWorker ()
114 Logger.Log.Info ("Starting KMail backend");
116 Stopwatch stopwatch = new Stopwatch ();
117 stopwatch.Start ();
119 // check if there is at all anything to crawl
120 if ( local_path == null && (!Directory.Exists (dimap_path))) {
121 GLib.Timeout.Add (60000, new GLib.TimeoutHandler (CheckForExistence));
122 Logger.Log.Debug ("KMail directories (local mail) " + dimap_path + " not found, will repoll.");
123 return;
126 Logger.Log.Debug ("Starting mail crawl");
127 State = QueryableState.Crawling;
128 if (local_path != null) {
129 local_indexer = new KMailIndexer (this, "local", local_path);
130 local_indexer.Crawl ();
132 // FIXME: parse kmailrc to get dimap account name
133 if (Directory.Exists (dimap_path)) {
134 dimap_indexer = new KMailIndexer (this, "dimap", dimap_path);
135 dimap_indexer.Crawl ();
137 State = QueryableState.Idle;
138 Logger.Log.Debug ("Mail crawl done");
140 if (! Inotify.Enabled) {
141 Scheduler.Task task = Scheduler.TaskFromHook (new Scheduler.TaskHook (CrawlHook));
142 task.Tag = "Crawling Maildir directories";
143 task.Source = this;
144 task.TriggerTime = DateTime.Now.AddSeconds (polling_interval_in_seconds);
145 ThisScheduler.Add (task);
148 stopwatch.Stop ();
149 Logger.Log.Info ("KMail driver worker thread done in {0}", stopwatch);
152 /**
153 * use this method to determine if we have anything to crawl and index
155 private bool CheckForExistence ()
157 local_path = GuessLocalFolderPath ();
158 if (local_path == null && (!Directory.Exists (dimap_path)))
159 return true;
161 StartWorker();
162 return false;
165 /////////////////////////////////////////////////////////////////////////////
167 override public string GetSnippet (string[] query_terms, Hit hit)
169 Logger.Log.Debug ("Fetching snippet for " + hit.Uri.LocalPath);
170 // FIXME: Also handle mbox emails
171 if (! hit.Uri.IsFile)
172 return null;
174 // Dont get snippets from attachments, they arent even indexed currently
175 if (hit.ParentUri != null)
176 return null;
178 int mail_fd = Mono.Unix.Native.Syscall.open (hit.Uri.LocalPath, Mono.Unix.Native.OpenFlags.O_RDONLY);
179 if (mail_fd == -1)
180 return null;
182 InitializeGMime ();
183 GMime.StreamFs stream = new GMime.StreamFs (mail_fd);
184 GMime.Parser parser = new GMime.Parser (stream);
185 GMime.Message message = parser.ConstructMessage ();
186 stream.Dispose ();
187 parser.Dispose ();
189 bool html = false;
190 string body = message.GetBody (true, out html);
191 // FIXME: Also handle snippets from html message parts - involves invoking html filter
192 if (html) {
193 Logger.Log.Debug ("No text/plain message part in " + hit.Uri);
194 message.Dispose ();
195 return null;
198 StringReader reader = new StringReader (body);
199 string snippet = SnippetFu.GetSnippet (query_terms, reader);
200 message.Dispose ();
202 return snippet;
205 /////////////////////////////////////////////////////////////////////////////
207 // FIXME: How to determine if an mbox hit is valid without scanning the whole file
209 public string Name {
210 get { return "KMail"; }
213 /**
214 * path of local maildir - mine is in ~/.Mail
215 * This is distribution specific. Mandrake puts kmail mails in
216 * ~/.Mail whereas default kmail folder location is ~/Mail
217 * I guess each distribution can fix this path as they know what is
218 * the path.
219 * It is possible to have the path specified in kmailrc. It might not
220 * be present, in which case try to play a guessing game.
221 * Till then, using a guesser to find out which of ~/.Mail and ~/Mail
222 * is valid.
223 * Guesses the kmail local folder path
224 * first try ~/.Mail, then try ~/Mail
225 * then try ~/.kde/share/apps/kmail/mail
227 private string GuessLocalFolderPath ()
229 string locationrc = GetLocalFolderPathFromKmailrc ();
230 //Logger.Log.Debug ("Reading kmail local-mail location from kmailrc: " +
231 // (locationrc == null ? "Unavailable" : locationrc));
232 string location1 = Path.Combine (PathFinder.HomeDir, "Mail");
233 string location2 = Path.Combine (PathFinder.HomeDir, ".Mail");
235 string location3 = Path.Combine (PathFinder.HomeDir, ".kde");
236 location3 = Path.Combine (location3, "share");
237 location3 = Path.Combine (location3, "apps");
238 location3 = Path.Combine (location3, "kmail");
239 location3 = Path.Combine (location3, "mail");
241 if (locationrc != null && GuessLocalFolder (locationrc))
242 return locationrc;
243 else if (GuessLocalFolder (location1))
244 return location1;
245 else if (GuessLocalFolder (location2))
246 return location2;
247 else if (GuessLocalFolder (location3))
248 return location3;
249 else
250 return null;
254 * to check if the path represents a kmail directory:
255 * for all directories and files named "ddd" and not starting with a '.',
256 * there should be matching index file named .ddd.index
258 private bool GuessLocalFolder (string path)
260 if (! Directory.Exists (path))
261 return false;
262 bool flag = true;
264 foreach (string subdirname in DirectoryWalker.GetDirectoryNames (path)) {
265 if (subdirname.StartsWith ("."))
266 continue;
267 // index-file name is of pattern .name.index
268 string indexfile = Path.Combine (path, "." + subdirname + ".index");
269 if (! File.Exists (indexfile)) {
270 flag = false;
271 Logger.Log.Warn ( "KMail backend: " +
272 path +
273 " contains a maildir directory but no corresponding index file. Probably not a KMail mail directory. Ignoring this location!");
274 break;
278 if (! flag)
279 return false;
281 foreach (FileInfo file in DirectoryWalker.GetFileInfos (path)) {
282 if (file.Name.StartsWith ("."))
283 continue;
284 // index-file name is of pattern .name.index
285 string indexfile = Path.Combine (path, "." + file.Name + ".index");
286 if (! File.Exists (indexfile)) {
287 flag = false;
288 Logger.Log.Warn ( "KMail backend: " +
289 path +
290 " contains an mbox file but no corresponding index file. Probably not a KMail mail directory. Ignoring this location!");
291 break;
295 return flag;
299 * tries to extract folder name from ~/.kde/share/config/kmailrc
301 private string GetLocalFolderPathFromKmailrc ()
303 string kmailrc = Path.Combine (PathFinder.HomeDir, ".kde");
304 kmailrc = Path.Combine (kmailrc, "share");
305 kmailrc = Path.Combine (kmailrc, "config");
306 kmailrc = Path.Combine (kmailrc, "kmailrc");
308 if (File.Exists (kmailrc)) {
309 StreamReader reader = new StreamReader (kmailrc);
310 string section = "";
311 string line;
313 try {
314 while ((line = reader.ReadLine ()) != null) {
315 if (line.StartsWith ("[") && line.EndsWith ("]")) {
316 section = line;
318 if (section == "[General]") {
319 if (line.StartsWith ("folders=") && line.Length > 8) {
320 return StringFu.ExpandEnvVariables (line.Substring(8));
324 } finally {
325 reader.Close ();
329 return null;