Remove debug statements from KCal backends.
[beagle.git] / Util / Conf.cs
bloba61235f3d25820b84bbbcdec066d6c47e56a5794
1 //
2 // Conf.cs
3 //
4 // Copyright (C) 2005 Novell, Inc.
5 //
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining a copy
9 // of this software and associated documentation files (the "Software"), to deal
10 // in the Software without restriction, including without limitation the rights
11 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 // copies of the Software, and to permit persons to whom the Software is
13 // furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in all
16 // 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 FROM,
23 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 // SOFTWARE.
27 using System;
28 using System.Collections;
29 using System.IO;
30 using System.Diagnostics;
31 using System.Reflection;
32 using System.Xml.Serialization;
33 using System.Text.RegularExpressions;
35 using Beagle.Util;
37 namespace Beagle.Util {
39 public class Conf {
41 // No instantiation
42 private Conf () { }
44 public static Hashtable Sections;
46 public static IndexingConfig Indexing = null;
47 public static DaemonConfig Daemon = null;
48 public static SearchingConfig Searching = null;
50 private static string configs_dir;
51 private static Hashtable mtimes;
52 private static Hashtable subscriptions;
54 private static bool watching_for_updates;
55 private static bool update_watch_present;
57 private static BindingFlags method_search_flags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod;
59 public delegate void ConfigUpdateHandler (Section section);
61 static Conf ()
63 Sections = new Hashtable (3);
64 mtimes = new Hashtable (3);
65 subscriptions = new Hashtable (3);
67 configs_dir = Path.Combine (PathFinder.StorageDir, "config");
68 if (!Directory.Exists (configs_dir))
69 Directory.CreateDirectory (configs_dir);
71 Conf.Load ();
74 public static void WatchForUpdates ()
76 // Make sure we don't try and watch for updates more than once
77 if (update_watch_present)
78 return;
80 if (Inotify.Enabled) {
81 Inotify.Subscribe (configs_dir, OnInotifyEvent, Inotify.EventType.Create | Inotify.EventType.CloseWrite);
82 } else {
83 // Poll for updates every 60 secs
84 GLib.Timeout.Add (60000, new GLib.TimeoutHandler (CheckForUpdates));
87 update_watch_present = true;
90 private static void OnInotifyEvent (Inotify.Watch watch, string path, string subitem, string srcpath, Inotify.EventType type)
92 if (subitem == "" || watching_for_updates == false)
93 return;
95 Load ();
98 private static bool CheckForUpdates ()
100 if (watching_for_updates)
101 Load ();
102 return true;
105 public static void Subscribe (Type type, ConfigUpdateHandler callback)
107 if (!subscriptions.ContainsKey (type))
108 subscriptions.Add (type, new ArrayList (1));
110 ArrayList callbacks = (ArrayList) subscriptions [type];
111 callbacks.Add (callback);
114 private static void NotifySubscribers (Section section)
116 Type type = section.GetType ();
117 ArrayList callbacks = (ArrayList) subscriptions [type];
119 if (callbacks == null)
120 return;
122 foreach (ConfigUpdateHandler callback in callbacks)
123 callback (section);
126 public static void Load ()
128 Load (false);
131 public static void Load (bool force)
133 Section temp;
135 // FIXME: Yeah
136 LoadFile (typeof (IndexingConfig), Indexing, out temp, force);
137 Indexing = (IndexingConfig) temp;
138 NotifySubscribers (Indexing);
140 LoadFile (typeof (DaemonConfig), Daemon, out temp, force);
141 Daemon = (DaemonConfig) temp;
142 NotifySubscribers (Daemon);
144 LoadFile (typeof (SearchingConfig), Searching, out temp, force);
145 Searching = (SearchingConfig) temp;
146 NotifySubscribers (Searching);
148 watching_for_updates = true;
151 public static void Save ()
153 Save (false);
156 public static void Save (bool force)
158 foreach (Section section in Sections.Values)
159 if (force || section.SaveNeeded)
160 SaveFile (section);
163 private static bool LoadFile (Type type, Section current, out Section section, bool force)
165 section = current;
166 object [] attrs = Attribute.GetCustomAttributes (type, typeof (ConfigSection));
167 if (attrs.Length == 0)
168 throw new ConfigException ("Could not find ConfigSection attribute on " + type);
170 string sectionname = ((ConfigSection) attrs [0]).Name;
171 string filename = sectionname + ".xml";
172 string filepath = Path.Combine (configs_dir, filename);
173 if (!File.Exists (filepath)) {
174 if (current == null)
175 ConstructDefaultSection (type, sectionname, out section);
176 return false;
179 if (!force && current != null && mtimes.ContainsKey (sectionname) &&
180 File.GetLastWriteTimeUtc (filepath).CompareTo ((DateTime) mtimes [sectionname]) <= 0)
181 return false;
183 Logger.Log.Debug ("Loading {0} from {1}", type, filename);
184 FileStream fs = null;
186 try {
187 fs = File.Open (filepath, FileMode.Open, FileAccess.Read, FileShare.Read);
188 XmlSerializer serializer = new XmlSerializer (type);
189 section = (Section) serializer.Deserialize (fs);
190 } catch (Exception e) {
191 Logger.Log.Error (e, "Could not load configuration from {0}:", filename);
192 if (fs != null)
193 fs.Close ();
194 if (current == null)
195 ConstructDefaultSection (type, sectionname, out section);
196 return false;
199 fs.Close ();
200 Sections.Remove (sectionname);
201 Sections.Add (sectionname, section);
202 mtimes.Remove (sectionname);
203 mtimes.Add (sectionname, File.GetLastWriteTimeUtc (filepath));
204 return true;
207 private static bool SaveFile (Section section)
209 Type type = section.GetType ();
210 object [] attrs = Attribute.GetCustomAttributes (type, typeof (ConfigSection));
211 if (attrs.Length == 0)
212 throw new ConfigException ("Could not find ConfigSection attribute on " + type);
214 string sectionname = ((ConfigSection) attrs [0]).Name;
215 string filename = sectionname + ".xml";
216 string filepath = Path.Combine (configs_dir, filename);
218 Logger.Log.Debug ("Saving {0} to {1}", type, filename);
219 FileStream fs = null;
221 try {
222 watching_for_updates = false;
223 fs = new FileStream (filepath, FileMode.Create);
224 XmlSerializer serializer = new XmlSerializer (type);
225 XmlFu.SerializeUtf8 (serializer, fs, section);
226 } catch (Exception e) {
227 if (fs != null)
228 fs.Close ();
229 Logger.Log.Error ("Could not save configuration to {0}: {1}", filename, e);
230 watching_for_updates = true;
231 return false;
234 fs.Close ();
235 mtimes.Remove (sectionname);
236 mtimes.Add (sectionname, File.GetLastWriteTimeUtc (filepath));
237 watching_for_updates = true;
238 return true;
241 private static void ConstructDefaultSection (Type type, string sectionname, out Section section)
243 ConstructorInfo ctor = type.GetConstructor (Type.EmptyTypes);
244 section = (Section) ctor.Invoke (null);
245 Sections.Remove (sectionname);
246 Sections.Add (sectionname, section);
249 // Lists all config file options in a hash table where key is option name,
250 // and value is description.
251 public static Hashtable GetOptions (Section section)
253 Hashtable options = new Hashtable ();
254 MemberInfo [] members = section.GetType ().GetMembers (method_search_flags);
256 // Find all of the methods ("options") inside the specified section
257 // object which have the ConfigOption attribute.
258 foreach (MemberInfo member in members) {
259 object [] attrs = member.GetCustomAttributes (typeof (ConfigOption), false);
260 if (attrs.Length > 0)
261 options.Add (member.Name, ((ConfigOption) attrs [0]).Description);
264 return options;
267 public static bool InvokeOption (Section section, string option, string [] args, out string output)
269 MethodInfo method = section.GetType ().GetMethod (option, method_search_flags);
270 if (method == null) {
271 string msg = String.Format ("No such method '{0}' for section '{1}'", option, section);
272 throw new ConfigException(msg);
274 object [] attrs = method.GetCustomAttributes (typeof (ConfigOption), false);
275 if (attrs.Length == 0) {
276 string msg = String.Format ("Method '{0}' is not a configurable option", option);
277 throw new ConfigException (msg);
280 // Check the required number of parameters have been provided
281 ConfigOption attr = (ConfigOption) attrs [0];
282 if (attr.Params > 0 && args.Length < attr.Params) {
283 string msg = String.Format ("Option '{0}' requires {1} parameter(s): {2}", option, attr.Params, attr.ParamsDescription);
284 throw new ConfigException (msg);
287 object [] methodparams = { null, args };
288 bool result = (bool) method.Invoke (section, methodparams);
289 output = (string) methodparams [0];
291 // Mark the section as save-needed if we just changed something
292 if (result && attr.IsMutator)
293 section.SaveNeeded = true;
295 return result;
298 [ConfigSection (Name="searching")]
299 public class SearchingConfig : Section {
301 private bool autostart = true;
302 public bool Autostart {
303 get { return autostart; }
304 set { autostart = value; }
307 private KeyBinding show_search_window_binding = new KeyBinding ("F12");
308 public KeyBinding ShowSearchWindowBinding {
309 get { return show_search_window_binding; }
310 set { show_search_window_binding = value; }
313 private int max_displayed = 5;
314 public int MaxDisplayed {
315 get { return max_displayed; }
316 set {
317 if (value <= 0)
318 max_displayed = 1;
319 else
320 max_displayed = value;
324 // BeagleSearch window position and dimension
325 // stored as percentage of screen co-ordinates
326 // to deal with change of resolution problem - hints from tberman
328 private float beagle_search_pos_x = 0;
329 public float BeaglePosX {
330 get { return beagle_search_pos_x; }
331 set { beagle_search_pos_x = value; }
334 private float beagle_search_pos_y = 0;
335 public float BeaglePosY {
336 get { return beagle_search_pos_y; }
337 set { beagle_search_pos_y = value; }
340 private float beagle_search_width = 0;
341 public float BeagleSearchWidth {
342 get { return beagle_search_width; }
343 set { beagle_search_width = value; }
346 private float beagle_search_height = 0;
347 public float BeagleSearchHeight {
348 get { return beagle_search_height; }
349 set { beagle_search_height = value; }
352 // ah!We want a Queue but Queue doesnt serialize *easily*
353 private ArrayList search_history = new ArrayList ();
354 public ArrayList SearchHistory {
355 get { return search_history; }
356 set { search_history = value; }
361 [ConfigSection (Name="daemon")]
362 public class DaemonConfig : Section {
363 private ArrayList static_queryables = new ArrayList ();
364 public ArrayList StaticQueryables {
365 get { return static_queryables; }
366 set { static_queryables = value; }
369 // By default, every backend is allowed.
370 // Only maintain a list of denied backends.
371 private ArrayList denied_backends = new ArrayList ();
372 public ArrayList DeniedBackends {
373 get { return denied_backends; }
374 set { denied_backends = value; }
377 private bool allow_static_backend = false; // by default, false
378 public bool AllowStaticBackend {
379 get { return allow_static_backend; }
380 // Don't really want to expose this, but serialization requires it
381 set { allow_static_backend = value; }
384 private bool index_synchronization = true;
385 public bool IndexSynchronization {
386 get { return index_synchronization; }
387 // Don't really want to expose this, but serialization requires it
388 set { index_synchronization = value; }
391 [ConfigOption (Description="Enable a backend", Params=1, ParamsDescription="Name of the backend to enable")]
392 internal bool AllowBackend (out string output, string [] args)
394 denied_backends.Remove (args [0]);
395 output = "Backend allowed (need to restart beagled for changes to take effect).";
396 return true;
399 [ConfigOption (Description="Disable a backend", Params=1, ParamsDescription="Name of the backend to disable")]
400 internal bool DenyBackend (out string output, string [] args)
402 denied_backends.Add (args [0]);
403 output = "Backend disabled (need to restart beagled for changes to take effect).";
404 return true;
407 private bool allow_root = false;
408 public bool AllowRoot {
409 get { return allow_root; }
410 set { allow_root = value; }
413 [ConfigOption (Description="Add a static queryable", Params=1, ParamsDescription="Index path")]
414 internal bool AddStaticQueryable (out string output, string [] args)
416 static_queryables.Add (args [0]);
417 output = "Static queryable added.";
418 return true;
421 [ConfigOption (Description="Remove a static queryable", Params=1, ParamsDescription="Index path")]
422 internal bool DelStaticQueryable (out string output, string [] args)
424 static_queryables.Remove (args [0]);
425 output = "Static queryable removed.";
426 return true;
429 [ConfigOption (Description="List user-specified static queryables", IsMutator=false)]
430 internal bool ListStaticQueryables (out string output, string [] args)
432 output = "User-specified static queryables:\n";
433 foreach (string index_path in static_queryables)
434 output += String.Format (" - {0}\n", index_path);
435 return true;
438 [ConfigOption (Description="Toggles whether static indexes will be enabled")]
439 internal bool ToggleAllowStaticBackend (out string output, string [] args)
441 allow_static_backend = !allow_static_backend;
442 output = "Static indexes are " + ((allow_static_backend) ? "enabled" : "disabled") + " (need to restart beagled for changes to take effect).";
443 return true;
446 [ConfigOption (Description="Toggles whether your indexes will be synchronized locally if your home directory is on a network device (eg. NFS/Samba)")]
447 internal bool ToggleIndexSynchronization (out string output, string [] args)
449 index_synchronization = !index_synchronization;
450 output = "Index Synchronization is " + ((index_synchronization) ? "enabled" : "disabled") + ".";
451 return true;
454 [ConfigOption (Description="Toggles whether Beagle can be run as root")]
455 internal bool ToggleAllowRoot (out string output, string [] args)
457 allow_root = ! allow_root;
458 if (allow_root)
459 output = "Beagle is now permitted to run as root";
460 else
461 output = "Beagle is no longer permitted to run as root";
462 return true;
466 [ConfigSection (Name="indexing")]
467 public class IndexingConfig : Section
469 private ArrayList roots = new ArrayList ();
470 [XmlArray]
471 [XmlArrayItem(ElementName="Root", Type=typeof(string))]
472 public ArrayList Roots {
473 get { return roots; }
474 set { roots = value; }
477 private bool index_home_dir = true;
478 public bool IndexHomeDir {
479 get { return index_home_dir; }
480 set { index_home_dir = value; }
483 private bool index_on_battery = true;
484 public bool IndexOnBattery {
485 get { return index_on_battery; }
486 set { index_on_battery = value; }
489 private ArrayList excludes = new ArrayList ();
490 [XmlArray]
491 [XmlArrayItem (ElementName="ExcludeItem", Type=typeof(ExcludeItem))]
492 public ArrayList Excludes {
493 get { return excludes; }
494 set { excludes = value; }
497 [ConfigOption (Description="List the indexing roots", IsMutator=false)]
498 internal bool ListRoots (out string output, string [] args)
500 output = "Current roots:\n";
501 if (this.index_home_dir == true)
502 output += " - Your home directory\n";
503 foreach (string root in roots)
504 output += " - " + root + "\n";
506 return true;
509 [ConfigOption (Description="Toggles whether your home directory is to be indexed as a root")]
510 internal bool IndexHome (out string output, string [] args)
512 if (index_home_dir)
513 output = "Your home directory will not be indexed.";
514 else
515 output = "Your home directory will be indexed.";
516 index_home_dir = !index_home_dir;
517 return true;
520 [ConfigOption (Description="Toggles whether any data should be indexed if the system is on battery")]
521 internal bool IndexWhileOnBattery (out string output, string [] args)
523 if (index_on_battery)
524 output = "Data will not be indexed while on battery.";
525 else
526 output = "Data will be indexed while on battery.";
527 index_on_battery = !index_on_battery;
528 return true;
531 [ConfigOption (Description="Add a root path to be indexed", Params=1, ParamsDescription="A path")]
532 internal bool AddRoot (out string output, string [] args)
534 roots.Add (args [0]);
535 output = "Root added.";
536 return true;
539 [ConfigOption (Description="Remove an indexing root", Params=1, ParamsDescription="A path")]
540 internal bool DelRoot (out string output, string [] args)
542 roots.Remove (args [0]);
543 output = "Root removed.";
544 return true;
547 [ConfigOption (Description="List user-specified resources to be excluded from indexing", IsMutator=false)]
548 internal bool ListExcludes (out string output, string [] args)
550 output = "User-specified resources to be excluded from indexing:\n";
551 foreach (ExcludeItem exclude_item in excludes)
552 output += String.Format (" - [{0}] {1}\n", exclude_item.Type.ToString (), exclude_item.Value);
553 return true;
556 [ConfigOption (Description="Add a resource to exclude from indexing", Params=2, ParamsDescription="A type [path/pattern/mailfolder], a path/pattern/name")]
557 internal bool AddExclude (out string output, string [] args)
559 ExcludeType type;
560 try {
561 type = (ExcludeType) Enum.Parse (typeof (ExcludeType), args [0], true);
562 } catch (Exception e) {
563 output = String.Format("Invalid type '{0}'. Valid types: Path, Pattern, MailFolder", args [0]);
564 return false;
567 excludes.Add (new ExcludeItem (type, args [1]));
568 output = "Exclude added.";
569 return true;
572 [ConfigOption (Description="Remove an excluded resource", Params=2, ParamsDescription="A type [path/pattern/mailfolder], a path/pattern/name")]
573 internal bool DelExclude (out string output, string [] args)
575 ExcludeType type;
576 try {
577 type = (ExcludeType) Enum.Parse (typeof (ExcludeType), args [0], true);
578 } catch (Exception e) {
579 output = String.Format("Invalid type '{0}'. Valid types: Path, Pattern, MailFolder", args [0]);
580 return false;
583 foreach (ExcludeItem item in excludes) {
584 if (item.Type != type || item.Value != args [1])
585 continue;
586 excludes.Remove (item);
587 output = "Exclude removed.";
588 return true;
591 output = "Could not find requested exclude to remove.";
592 return false;
597 public class Section {
598 [XmlIgnore]
599 public bool SaveNeeded = false;
602 private class ConfigOption : Attribute {
603 public string Description;
604 public int Params;
605 public string ParamsDescription;
606 public bool IsMutator = true;
609 private class ConfigSection : Attribute {
610 public string Name;
613 public class ConfigException : Exception {
614 public ConfigException (string msg) : base (msg) { }
619 //////////////////////////////////////////////////////////////////////
621 public enum ExcludeType {
622 Path,
623 Pattern,
624 MailFolder
627 public class ExcludeItem {
629 private ExcludeType type;
630 private string val;
632 [XmlAttribute]
633 public ExcludeType Type {
634 get { return type; }
635 set { type = value; }
638 private string exactMatch;
639 private string prefix;
640 private string suffix;
641 private Regex regex;
643 [XmlAttribute]
644 public string Value {
645 get { return val; }
646 set {
647 switch (type) {
648 case ExcludeType.Path:
649 case ExcludeType.MailFolder:
650 prefix = value;
651 break;
653 case ExcludeType.Pattern:
654 if (value.StartsWith ("/") && value.EndsWith ("/")) {
655 regex = new Regex (value.Substring (1, value.Length - 2));
656 break;
659 int i = value.IndexOf ('*');
660 if (i == -1) {
661 exactMatch = value;
662 } else {
663 if (i > 0)
664 prefix = value.Substring (0, i);
665 if (i < value.Length-1)
666 suffix = value.Substring (i+1);
668 break;
671 val = value;
675 public ExcludeItem () {}
677 public ExcludeItem (ExcludeType type, string value) {
678 this.Type = type;
679 this.Value = value;
682 public bool IsMatch (string param)
684 switch (Type) {
685 case ExcludeType.Path:
686 case ExcludeType.MailFolder:
687 if (prefix != null && ! param.StartsWith (prefix))
688 return false;
690 return true;
692 case ExcludeType.Pattern:
693 if (exactMatch != null)
694 return param == exactMatch;
695 if (prefix != null && ! param.StartsWith (prefix))
696 return false;
697 if (suffix != null && ! param.EndsWith (suffix))
698 return false;
699 if (regex != null && ! regex.IsMatch (param))
700 return false;
702 return true;
705 return false;
708 public override bool Equals (object obj)
710 ExcludeItem exclude = obj as ExcludeItem;
711 return (exclude != null && exclude.Type == type && exclude.Value == val);
714 public override int GetHashCode ()
716 return (this.Value.GetHashCode () ^ (int) this.Type);
721 //////////////////////////////////////////////////////////////////////
723 public class KeyBinding {
724 public string Key;
726 [XmlAttribute]
727 public bool Ctrl = false;
728 [XmlAttribute]
729 public bool Alt = false;
731 public KeyBinding () {}
732 public KeyBinding (string key) : this (key, false, false) {}
734 public KeyBinding (string key, bool ctrl, bool alt)
736 Key = key;
737 Ctrl = ctrl;
738 Alt = alt;
741 public override string ToString ()
743 string result = "";
745 if (Ctrl)
746 result += "<Ctrl>";
747 if (Alt)
748 result += "<Alt>";
750 result += Key;
752 return result;
755 public string ToReadableString ()
757 return ToString ().Replace (">", "-").Replace ("<", "");