* tools/Info.cs: Add --list-backends, --list-static-indexes to
[beagle.git] / Util / Conf.cs
blob097069f9a8eca75b00df6212b15d68f6ca8da9dc
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 //#if ENABLE_WEBSERVICES
51 public static NetworkingConfig Networking = null;
52 public static WebServicesConfig WebServices = null;
53 //#endif
54 private static string configs_dir;
55 private static Hashtable mtimes;
56 private static Hashtable subscriptions;
58 private static bool watching_for_updates;
59 private static bool update_watch_present;
61 private static BindingFlags method_search_flags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod;
63 public delegate void ConfigUpdateHandler (Section section);
65 static Conf ()
67 Sections = new Hashtable (3);
68 mtimes = new Hashtable (3);
69 subscriptions = new Hashtable (3);
71 configs_dir = Path.Combine (PathFinder.StorageDir, "config");
72 if (!Directory.Exists (configs_dir))
73 Directory.CreateDirectory (configs_dir);
75 Conf.Load ();
78 public static void WatchForUpdates ()
80 // Make sure we don't try and watch for updates more than once
81 if (update_watch_present)
82 return;
84 if (Inotify.Enabled) {
85 Inotify.Subscribe (configs_dir, OnInotifyEvent, Inotify.EventType.Create | Inotify.EventType.CloseWrite);
86 } else {
87 // Poll for updates every 60 secs
88 GLib.Timeout.Add (60000, new GLib.TimeoutHandler (CheckForUpdates));
91 update_watch_present = true;
94 private static void OnInotifyEvent (Inotify.Watch watch, string path, string subitem, string srcpath, Inotify.EventType type)
96 if (subitem == "" || watching_for_updates == false)
97 return;
99 Load ();
102 private static bool CheckForUpdates ()
104 if (watching_for_updates)
105 Load ();
106 return true;
109 public static void Subscribe (Type type, ConfigUpdateHandler callback)
111 if (!subscriptions.ContainsKey (type))
112 subscriptions.Add (type, new ArrayList (1));
114 ArrayList callbacks = (ArrayList) subscriptions [type];
115 callbacks.Add (callback);
118 private static void NotifySubscribers (Section section)
120 Type type = section.GetType ();
121 ArrayList callbacks = (ArrayList) subscriptions [type];
123 if (callbacks == null)
124 return;
126 foreach (ConfigUpdateHandler callback in callbacks)
127 callback (section);
130 public static void Load ()
132 Load (false);
135 public static void Load (bool force)
137 Section temp;
139 // FIXME: Yeah
140 LoadFile (typeof (IndexingConfig), Indexing, out temp, force);
141 Indexing = (IndexingConfig) temp;
142 NotifySubscribers (Indexing);
144 LoadFile (typeof (DaemonConfig), Daemon, out temp, force);
145 Daemon = (DaemonConfig) temp;
146 NotifySubscribers (Daemon);
148 LoadFile (typeof (SearchingConfig), Searching, out temp, force);
149 Searching = (SearchingConfig) temp;
150 NotifySubscribers (Searching);
152 //#if ENABLE_WEBSERVICES
153 LoadFile (typeof (NetworkingConfig), Networking, out temp, force);
154 Networking = (NetworkingConfig) temp;
155 NotifySubscribers (Networking);
157 LoadFile (typeof (WebServicesConfig), WebServices, out temp, force);
158 WebServices = (WebServicesConfig) temp;
159 NotifySubscribers (WebServices);
160 //#endif
162 watching_for_updates = true;
165 public static void Save ()
167 Save (false);
170 public static void Save (bool force)
172 foreach (Section section in Sections.Values)
173 if (force || section.SaveNeeded)
174 SaveFile (section);
177 private static bool LoadFile (Type type, Section current, out Section section, bool force)
179 section = current;
180 object [] attrs = Attribute.GetCustomAttributes (type, typeof (ConfigSection));
181 if (attrs.Length == 0)
182 throw new ConfigException ("Could not find ConfigSection attribute on " + type);
184 string sectionname = ((ConfigSection) attrs [0]).Name;
185 string filename = sectionname + ".xml";
186 string filepath = Path.Combine (configs_dir, filename);
187 if (!File.Exists (filepath)) {
188 if (current == null)
189 ConstructDefaultSection (type, sectionname, out section);
190 return false;
193 if (!force && current != null && mtimes.ContainsKey (sectionname) &&
194 File.GetLastWriteTimeUtc (filepath).CompareTo ((DateTime) mtimes [sectionname]) <= 0)
195 return false;
197 Logger.Log.Debug ("Loading {0} from {1}", type, filename);
198 FileStream fs = null;
200 try {
201 fs = File.Open (filepath, FileMode.Open, FileAccess.Read, FileShare.Read);
202 XmlSerializer serializer = new XmlSerializer (type);
203 section = (Section) serializer.Deserialize (fs);
204 } catch (Exception e) {
205 Logger.Log.Error ("Could not load configuration from {0}: {1}", filename, e.Message);
206 if (fs != null)
207 fs.Close ();
208 if (current == null)
209 ConstructDefaultSection (type, sectionname, out section);
210 return false;
213 fs.Close ();
214 Sections.Remove (sectionname);
215 Sections.Add (sectionname, section);
216 mtimes.Remove (sectionname);
217 mtimes.Add (sectionname, File.GetLastWriteTimeUtc (filepath));
218 return true;
221 private static bool SaveFile (Section section)
223 Type type = section.GetType ();
224 object [] attrs = Attribute.GetCustomAttributes (type, typeof (ConfigSection));
225 if (attrs.Length == 0)
226 throw new ConfigException ("Could not find ConfigSection attribute on " + type);
228 string sectionname = ((ConfigSection) attrs [0]).Name;
229 string filename = sectionname + ".xml";
230 string filepath = Path.Combine (configs_dir, filename);
232 Logger.Log.Debug ("Saving {0} to {1}", type, filename);
233 FileStream fs = null;
235 try {
236 watching_for_updates = false;
237 fs = new FileStream (filepath, FileMode.Create);
238 XmlSerializer serializer = new XmlSerializer (type);
239 XmlFu.SerializeUtf8 (serializer, fs, section);
240 } catch (Exception e) {
241 if (fs != null)
242 fs.Close ();
243 Logger.Log.Error ("Could not save configuration to {0}: {1}", filename, e);
244 watching_for_updates = true;
245 return false;
248 fs.Close ();
249 mtimes.Remove (sectionname);
250 mtimes.Add (sectionname, File.GetLastWriteTimeUtc (filepath));
251 watching_for_updates = true;
252 return true;
255 private static void ConstructDefaultSection (Type type, string sectionname, out Section section)
257 ConstructorInfo ctor = type.GetConstructor (Type.EmptyTypes);
258 section = (Section) ctor.Invoke (null);
259 Sections.Remove (sectionname);
260 Sections.Add (sectionname, section);
263 // Lists all config file options in a hash table where key is option name,
264 // and value is description.
265 public static Hashtable GetOptions (Section section)
267 Hashtable options = new Hashtable ();
268 MemberInfo [] members = section.GetType ().GetMembers (method_search_flags);
270 // Find all of the methods ("options") inside the specified section
271 // object which have the ConfigOption attribute.
272 foreach (MemberInfo member in members) {
273 object [] attrs = member.GetCustomAttributes (typeof (ConfigOption), false);
274 if (attrs.Length > 0)
275 options.Add (member.Name, ((ConfigOption) attrs [0]).Description);
278 return options;
281 public static bool InvokeOption (Section section, string option, string [] args, out string output)
283 MethodInfo method = section.GetType ().GetMethod (option, method_search_flags);
284 if (method == null) {
285 string msg = String.Format ("No such method '{0}' for section '{1}'", option, section);
286 throw new ConfigException(msg);
288 object [] attrs = method.GetCustomAttributes (typeof (ConfigOption), false);
289 if (attrs.Length == 0) {
290 string msg = String.Format ("Method '{0}' is not a configurable option", option);
291 throw new ConfigException (msg);
294 // Check the required number of parameters have been provided
295 ConfigOption attr = (ConfigOption) attrs [0];
296 if (attr.Params > 0 && args.Length < attr.Params) {
297 string msg = String.Format ("Option '{0}' requires {1} parameter(s): {2}", option, attr.Params, attr.ParamsDescription);
298 throw new ConfigException (msg);
301 object [] methodparams = { null, args };
302 bool result = (bool) method.Invoke (section, methodparams);
303 output = (string) methodparams [0];
305 // Mark the section as save-needed if we just changed something
306 if (result && attr.IsMutator)
307 section.SaveNeeded = true;
309 return result;
312 [ConfigSection (Name="searching")]
313 public class SearchingConfig : Section {
315 private bool autostart = true;
316 public bool Autostart {
317 get { return autostart; }
318 set { autostart = value; }
321 private KeyBinding show_search_window_binding = new KeyBinding ("F12");
322 public KeyBinding ShowSearchWindowBinding {
323 get { return show_search_window_binding; }
324 set { show_search_window_binding = value; }
327 private int max_displayed = 5;
328 public int MaxDisplayed {
329 get { return max_displayed; }
330 set {
331 if (value <= 0)
332 max_displayed = 1;
333 else
334 max_displayed = value;
338 // Best window position and dimension
339 // stored as percentage of screen co-ordinates
340 // to deal with change of resolution problem - hints from tberman
342 private float best_pos_x = 0;
343 public float BestPosX {
344 get { return best_pos_x; }
345 set { best_pos_x = value; }
348 private float best_pos_y = 0;
349 public float BestPosY {
350 get { return best_pos_y; }
351 set { best_pos_y = value; }
354 // dont explicitly set height on first run
355 private float best_width = 0;
356 public float BestWidth {
357 get { return best_width; }
358 set { best_width = value; }
361 private float best_height = 0; // -ditto-
362 public float BestHeight {
363 get { return best_height; }
364 set { best_height = value; }
367 // FIXME Change names when Holmes is released under new name
368 // Holmes window position and dimension
370 private float holmes_pos_x = 0;
371 public float HolmesPosX {
372 get { return holmes_pos_x; }
373 set { holmes_pos_x = value; }
376 private float holmes_pos_y = 0;
377 public float HolmesPosY {
378 get { return holmes_pos_y; }
379 set { holmes_pos_y = value; }
382 private float holmes_width = 0;
383 public float HolmesWidth {
384 get { return holmes_width; }
385 set { holmes_width = value; }
388 private float holmes_height = 0;
389 public float HolmesHeight {
390 get { return holmes_height; }
391 set { holmes_height = value; }
394 // ah!We want a Queue but Queue doesnt serialize *easily*
395 private ArrayList search_history = new ArrayList ();
396 public ArrayList SearchHistory {
397 get { return search_history; }
398 set { search_history = value; }
403 [ConfigSection (Name="daemon")]
404 public class DaemonConfig : Section {
405 private ArrayList static_queryables = new ArrayList ();
406 public ArrayList StaticQueryables {
407 get { return static_queryables; }
408 set { static_queryables = value; }
411 // By default, every backend is allowed.
412 // Only maintain a list of denied backends.
413 private ArrayList denied_backends = new ArrayList ();
414 public ArrayList DeniedBackends {
415 get { return denied_backends; }
416 set { denied_backends = value; }
419 private bool allow_static_backend = false; // by default, false
420 public bool AllowStaticBackend {
421 get { return allow_static_backend; }
422 // Don't really want to expose this, but serialization requires it
423 set { allow_static_backend = value; }
426 private bool index_synchronization = true;
427 public bool IndexSynchronization {
428 get { return index_synchronization; }
429 // Don't really want to expose this, but serialization requires it
430 set { index_synchronization = value; }
433 [ConfigOption (Description="Enable a backend", Params=1, ParamsDescription="Name of the backend to enable")]
434 internal bool AllowBackend (out string output, string [] args)
436 denied_backends.Remove (args [0]);
437 output = "Backend allowed (need to restart beagled for changes to take effect).";
438 return true;
441 [ConfigOption (Description="Disable a backend", Params=1, ParamsDescription="Name of the backend to disable")]
442 internal bool DenyBackend (out string output, string [] args)
444 denied_backends.Add (args [0]);
445 output = "Backend disabled (need to restart beagled for changes to take effect).";
446 return true;
449 private bool allow_root = false;
450 public bool AllowRoot {
451 get { return allow_root; }
452 set { allow_root = value; }
455 [ConfigOption (Description="Add a static queryable", Params=1, ParamsDescription="Index path")]
456 internal bool AddStaticQueryable (out string output, string [] args)
458 static_queryables.Add (args [0]);
459 output = "Static queryable added.";
460 return true;
463 [ConfigOption (Description="Remove a static queryable", Params=1, ParamsDescription="Index path")]
464 internal bool DelStaticQueryable (out string output, string [] args)
466 static_queryables.Remove (args [0]);
467 output = "Static queryable removed.";
468 return true;
471 [ConfigOption (Description="List user-specified static queryables", IsMutator=false)]
472 internal bool ListStaticQueryables (out string output, string [] args)
474 output = "User-specified static queryables:\n";
475 foreach (string index_path in static_queryables)
476 output += String.Format (" - {0}\n", index_path);
477 return true;
480 [ConfigOption (Description="Toggles whether static indexes will be enabled")]
481 internal bool ToggleAllowStaticBackend (out string output, string [] args)
483 allow_static_backend = !allow_static_backend;
484 output = "Static indexes are " + ((allow_static_backend) ? "enabled" : "disabled") + " (need to restart beagled for changes to take effect).";
485 return true;
488 [ConfigOption (Description="Toggles whether your indexes will be synchronized locally if your home directory is on a network device (eg. NFS/Samba)")]
489 internal bool ToggleIndexSynchronization (out string output, string [] args)
491 index_synchronization = !index_synchronization;
492 output = "Index Synchronization is " + ((index_synchronization) ? "enabled" : "disabled") + ".";
493 return true;
496 [ConfigOption (Description="Toggles whether Beagle can be run as root")]
497 internal bool ToggleAllowRoot (out string output, string [] args)
499 allow_root = ! allow_root;
500 if (allow_root)
501 output = "Beagle is now permitted to run as root";
502 else
503 output = "Beagle is no longer permitted to run as root";
504 return true;
508 [ConfigSection (Name="indexing")]
509 public class IndexingConfig : Section
511 private ArrayList roots = new ArrayList ();
512 [XmlArray]
513 [XmlArrayItem(ElementName="Root", Type=typeof(string))]
514 public ArrayList Roots {
515 get { return roots; }
516 set { roots = value; }
519 private bool index_home_dir = true;
520 public bool IndexHomeDir {
521 get { return index_home_dir; }
522 set { index_home_dir = value; }
525 private ArrayList excludes = new ArrayList ();
526 [XmlArray]
527 [XmlArrayItem (ElementName="ExcludeItem", Type=typeof(ExcludeItem))]
528 public ArrayList Excludes {
529 get { return excludes; }
530 set { excludes = value; }
533 [ConfigOption (Description="List the indexing roots", IsMutator=false)]
534 internal bool ListRoots (out string output, string [] args)
536 output = "Current roots:\n";
537 if (this.index_home_dir == true)
538 output += " - Your home directory\n";
539 foreach (string root in roots)
540 output += " - " + root + "\n";
542 return true;
545 [ConfigOption (Description="Toggles whether your home directory is to be indexed as a root")]
546 internal bool IndexHome (out string output, string [] args)
548 if (index_home_dir)
549 output = "Your home directory will not be indexed.";
550 else
551 output = "Your home directory will be indexed.";
552 index_home_dir = !index_home_dir;
553 return true;
556 [ConfigOption (Description="Add a root path to be indexed", Params=1, ParamsDescription="A path")]
557 internal bool AddRoot (out string output, string [] args)
559 roots.Add (args [0]);
560 output = "Root added.";
561 return true;
564 [ConfigOption (Description="Remove an indexing root", Params=1, ParamsDescription="A path")]
565 internal bool DelRoot (out string output, string [] args)
567 roots.Remove (args [0]);
568 output = "Root removed.";
569 return true;
572 [ConfigOption (Description="List user-specified resources to be excluded from indexing", IsMutator=false)]
573 internal bool ListExcludes (out string output, string [] args)
575 output = "User-specified resources to be excluded from indexing:\n";
576 foreach (ExcludeItem exclude_item in excludes)
577 output += String.Format (" - [{0}] {1}\n", exclude_item.Type.ToString (), exclude_item.Value);
578 return true;
581 [ConfigOption (Description="Add a resource to exclude from indexing", Params=2, ParamsDescription="A type [path/pattern/mailfolder], a path/pattern/name")]
582 internal bool AddExclude (out string output, string [] args)
584 ExcludeType type;
585 try {
586 type = (ExcludeType) Enum.Parse (typeof (ExcludeType), args [0], true);
587 } catch (Exception e) {
588 output = String.Format("Invalid type '{0}'. Valid types: Path, Pattern, MailFolder", args [0]);
589 return false;
592 excludes.Add (new ExcludeItem (type, args [1]));
593 output = "Exclude added.";
594 return true;
597 [ConfigOption (Description="Remove an excluded resource", Params=2, ParamsDescription="A type [path/pattern/mailfolder], a path/pattern/name")]
598 internal bool DelExclude (out string output, string [] args)
600 ExcludeType type;
601 try {
602 type = (ExcludeType) Enum.Parse (typeof (ExcludeType), args [0], true);
603 } catch (Exception e) {
604 output = String.Format("Invalid type '{0}'. Valid types: Path, Pattern, MailFolder", args [0]);
605 return false;
608 foreach (ExcludeItem item in excludes) {
609 if (item.Type != type || item.Value != args [1])
610 continue;
611 excludes.Remove (item);
612 output = "Exclude removed.";
613 return true;
616 output = "Could not find requested exclude to remove.";
617 return false;
622 //#if ENABLE_WEBSERVICES
623 [ConfigSection (Name="webservices")]
624 public class WebServicesConfig: Section
626 private ArrayList publicFolders = new ArrayList ();
627 [XmlArray]
628 [XmlArrayItem(ElementName="PublicFolders", Type=typeof(string))]
629 public ArrayList PublicFolders {
630 get { return publicFolders; }
631 set { publicFolders = value; }
634 private bool allowGlobalAccess = true;
635 public bool AllowGlobalAccess {
636 get { return allowGlobalAccess; }
637 set { allowGlobalAccess = value; }
640 [ConfigOption (Description="List the public folders", IsMutator=false)]
641 internal bool ListPublicFolders(out string output, string [] args)
643 output = "Current list of public folders:\n";
645 foreach (string pf in publicFolders)
646 output += " - " + pf + "\n";
648 return true;
651 [ConfigOption (Description="Check current configuration of global access to Beagle web-services", IsMutator=false)]
652 internal bool CheckGlobalAccess(out string output, string [] args)
654 if (allowGlobalAccess)
655 output = "Global Access to Beagle WebServices is currently ENABLED.";
656 else
657 output = "Global Access to Beagle WebServices is currently DISABLED.";
659 return true;
662 [ConfigOption (Description="Enable/Disable global access to Beagle web-services")]
663 internal bool SwitchGlobalAccess (out string output, string [] args)
665 allowGlobalAccess = !allowGlobalAccess;
667 if (allowGlobalAccess)
668 output = "Global Access to Beagle WebServices now ENABLED.";
669 else
670 output = "Global Access to Beagle WebServices now DISABLED.";
672 return true;
675 [ConfigOption (Description="Add public web-service access to a folder", Params=1, ParamsDescription="A path")]
676 internal bool AddPublicFolder (out string output, string [] args)
678 publicFolders.Add (args [0]);
679 output = "PublicFolder " + args[0] + " added.";
680 return true;
683 [ConfigOption (Description="Remove public web-service access to a folder", Params=1, ParamsDescription="A path")]
684 internal bool DelPublicFolder (out string output, string [] args)
686 publicFolders.Remove (args [0]);
687 output = "PublicFolder " + args[0] + " removed.";
688 return true;
693 [ConfigSection (Name="networking")]
694 public class NetworkingConfig: Section
696 private ArrayList netBeagleNodes = new ArrayList ();
698 [XmlArray]
699 [XmlArrayItem(ElementName="NetBeagleNodes", Type=typeof(string))]
700 public ArrayList NetBeagleNodes {
701 get { return netBeagleNodes; }
702 set { netBeagleNodes = value; }
705 [ConfigOption (Description="List Networked Beagle Daemons to query", IsMutator=false)]
706 internal bool ListBeagleNodes (out string output, string [] args)
708 output = "Current list of Networked Beagle Daemons to query:\n";
710 foreach (string nb in netBeagleNodes)
711 output += " - " + nb + "\n";
713 return true;
716 [ConfigOption (Description="Add a Networked Beagle Daemon to query", Params=1, ParamsDescription="HostName:PortNo")]
717 internal bool AddBeagleNode (out string output, string [] args)
719 string node = args[0];
721 if (((string[])node.Split(':')).Length < 2)
722 node = args [0].Trim() + ":8888";
724 netBeagleNodes.Add(node);
725 output = "Networked Beagle Daemon \"" + node +"\" added.";
726 return true;
729 [ConfigOption (Description="Remove a configured Networked Beagle Daemon", Params=1, ParamsDescription="HostName:PortNo")]
730 internal bool DelBeagleNode (out string output, string [] args)
732 string node = args[0];
734 if (((string[])node.Split(':')).Length < 2)
735 node = args [0].Trim() + ":8888";
737 netBeagleNodes.Remove(node);
738 output = "Networked Beagle Daemon \"" + node +"\" removed.";
739 return true;
742 //#endif
744 public class Section {
745 [XmlIgnore]
746 public bool SaveNeeded = false;
749 private class ConfigOption : Attribute {
750 public string Description;
751 public int Params;
752 public string ParamsDescription;
753 public bool IsMutator = true;
756 private class ConfigSection : Attribute {
757 public string Name;
760 public class ConfigException : Exception {
761 public ConfigException (string msg) : base (msg) { }
766 //////////////////////////////////////////////////////////////////////
768 public enum ExcludeType {
769 Path,
770 Pattern,
771 MailFolder
774 public class ExcludeItem {
776 private ExcludeType type;
777 private string val;
779 [XmlAttribute]
780 public ExcludeType Type {
781 get { return type; }
782 set { type = value; }
785 private string exactMatch;
786 private string prefix;
787 private string suffix;
788 private Regex regex;
790 [XmlAttribute]
791 public string Value {
792 get { return val; }
793 set {
794 switch (type) {
795 case ExcludeType.Path:
796 case ExcludeType.MailFolder:
797 prefix = value;
798 break;
800 case ExcludeType.Pattern:
801 if (value.StartsWith ("/") && value.EndsWith ("/")) {
802 regex = new Regex (value.Substring (1, value.Length - 2));
803 break;
806 int i = value.IndexOf ('*');
807 if (i == -1) {
808 exactMatch = value;
809 } else {
810 if (i > 0)
811 prefix = value.Substring (0, i);
812 if (i < value.Length-1)
813 suffix = value.Substring (i+1);
815 break;
818 val = value;
822 public ExcludeItem () {}
824 public ExcludeItem (ExcludeType type, string value) {
825 this.Type = type;
826 this.Value = value;
829 public bool IsMatch (string param)
831 switch (Type) {
832 case ExcludeType.Path:
833 case ExcludeType.MailFolder:
834 if (prefix != null && ! param.StartsWith (prefix))
835 return false;
837 return true;
839 case ExcludeType.Pattern:
840 if (exactMatch != null)
841 return param == exactMatch;
842 if (prefix != null && ! param.StartsWith (prefix))
843 return false;
844 if (suffix != null && ! param.EndsWith (suffix))
845 return false;
846 if (regex != null && ! regex.IsMatch (param))
847 return false;
849 return true;
852 return false;
855 public override bool Equals (object obj)
857 ExcludeItem exclude = obj as ExcludeItem;
858 return (exclude != null && exclude.Type == type && exclude.Value == val);
861 public override int GetHashCode ()
863 return (this.Value.GetHashCode () ^ (int) this.Type);
868 //////////////////////////////////////////////////////////////////////
870 public class KeyBinding {
871 public string Key;
873 [XmlAttribute]
874 public bool Ctrl = false;
875 [XmlAttribute]
876 public bool Alt = false;
878 public KeyBinding () {}
879 public KeyBinding (string key) : this (key, false, false) {}
881 public KeyBinding (string key, bool ctrl, bool alt)
883 Key = key;
884 Ctrl = ctrl;
885 Alt = alt;
888 public override string ToString ()
890 string result = "";
892 if (Ctrl)
893 result += "<Ctrl>";
894 if (Alt)
895 result += "<Alt>";
897 result += Key;
899 return result;
902 public string ToReadableString ()
904 return ToString ().Replace (">", "-").Replace ("<", "");