
/* Gnome Encfs Manager (GEncfsM)
 * Copyright (C) 2011 Moritz Molch <mail@moritzmolch.de>
 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

using Encfs;
using Gtk;
using Gee;
using Posix;
using Secret;

namespace GnomeEncfsManager {
	public delegate void IntStringCallback( int status, string message );
	
    public static ArrayList<Stash> stashes;
    public static GLib.Settings settings;
    private static const string settings_path = "com.libertyzero.gnome-encfs-manager";
    private static const string dbus_name = "com.libertyzero.GnomeEncfsManager";
    private static const string dbus_path = "/com/libertyzero/GnomeEncfsManager";
    private static ulong sighandler;
    private static bool no_reload = false;
    
    private static SimpleConf options;
    
    private static bool cliFlagNoStartupMount = false;
    private static bool cliFlagReplace = false;
	public static ArrayList<string> stash_var_list;
	public static ArrayList<string> pref_var_list;
	
	private static Mutex mutex;
	private static LinkedList<string> busy_messages;
	private static Mutex busy_counter_mutex;
	public static Mutex stashes_mutex;
	
	public static DBusService dbus_service;
    public static int dbus_delay;
    
    public static HashMap<string, string> script_variables;
		

    public static int main(string[] args) {
        Posix.signal(Posix.SIGTERM, (sig) => {
            quit();
        });

        Environment.set_variable("PATH",
                                 Path.build_path(Path.DIR_SEPARATOR_S, Environment.get_home_dir(),".local","share","gnome-encfs-manager", "scripts")+":"+
                                 get_data_path()+"/scripts:"+
                                 Environment.get_variable("PATH"),
                                 true);
    							 
    	mutex = new Mutex();
    	stashes_mutex = new Mutex();
    	busy_counter_mutex = new Mutex();
    	busy_messages = new LinkedList<string> ();
    	dbus_delay = 0;
    	MountDialog.queue = new LinkedList<Stash> ();
    	script_variables = new HashMap<string, string> ();
    	
    	stash_var_list = new ArrayList<string> ();
    	stash_var_list.add("mount_directory");
    	stash_var_list.add("mount_on_startup");
    	stash_var_list.add("mount_when_available");
    	stash_var_list.add("allow_other");
    	stash_var_list.add("idle_unmount");
    	stash_var_list.add("idle_unmount_time");
    	stash_var_list.add("use_keyring");
    	stash_var_list.add("show_in_tray");
    	stash_var_list.add("encfs_config_file");
    	stash_var_list.add("keep_mount_directory");
    	stash_var_list.add("stash_group");
    	stash_var_list.add("open_file_manager");
    	foreach (string opt in ConfigureStashAdvanced.options) {
    		stash_var_list.add(opt);
    	}
		
    	pref_var_list = new ArrayList<string> ();
    	pref_var_list.add("autostart");
    	pref_var_list.add("show_trayicon");
    	pref_var_list.add("start_minimized");
    	pref_var_list.add("unmount_on_logout");
    	pref_var_list.add("mount_dir_base");
    	pref_var_list.add("force_traysystem");
    	foreach (string opt in PreferencesAdvanced.options) {
    		pref_var_list.add(opt);
    	}
		
        settings = new GLib.Settings(settings_path);
        
		options = new SimpleConf();
        options.parse(settings.get_string("advanced-options"));

    	processCliFlags(args);
    	
        //Gdk.threads_init();
        Gtk.init(ref args);
    	
		Stash.init();
		load_stashes();
		AddStash.init();
		
        sighandler = settings.changed.connect(on_settings_changed);
    	
    	GLib.BusNameOwnerFlags flags = BusNameOwnerFlags.ALLOW_REPLACEMENT;
    	//if (cliFlagReplace) flags |= BusNameOwnerFlags.REPLACE;
    	Bus.own_name(	BusType.SESSION,
    					dbus_name,
    					flags,
    					(conn) => {
							try {
							    dbus_service = new DBusService();
    						    conn.register_object( dbus_path,
    											  dbus_service );
    						} catch (GLib.IOError e) {
								GLib.stderr.printf("%s\n", e.message);
							}
    					},
    					(conn) => {
							startup();
    					},
    					() => {
    						if (!cliFlagReplace) {
    							GLib.stdout.printf("Another instance is already running.\n");
    							Gtk.main_quit();
    							Posix.exit(0);
    						} else {
    						    startup();
    						}
    					}
    	);

        Gtk.main();
        return 0;
    }
    
    public bool startup_done = false;
    public static void startup() {
        if (startup_done) return;
        //GLib.stdout.printf("startup()\n");
        
        startup_done = true;
        GLib.Intl.setlocale(GLib.LocaleCategory.ALL, "");
        GLib.Intl.bindtextdomain(Config.GETTEXT_PACKAGE, Config.PACKAGE_LOCALE_DIR);
        GLib.Intl.textdomain(Config.GETTEXT_PACKAGE);
        
		session_init();

		bool first_start = false;
		if ( (settings.get_string("last-version") == "") && (stashes.size == 0)) {
			first_start = true;
			Preferences.autostart_set_enabled(true);
		}
		settings.set_string("last-version", Config.VERSION.split("~")[0]);

		if (settings.get_boolean("show-trayicon")) {
			TrayIcon.show();
		}

		if ( first_start || (!settings.get_boolean("start-minimized")) || (!settings.get_boolean("show-trayicon"))) {
			Manager.show();
		}
							 
		foreach (Stash s in stashes) {
			if (s.autoMount) {
				MountDialog.mount(s);
			}
		}
		
		if (first_start) {
		    try {
			    Process.spawn_command_line_async("first-run");    
			} catch (GLib.Error e) {
                GLib.stderr.printf("%s\n", e.message);
			}
		}
    }

    public static int busy( string message ) {
    	int msgid = 0;
    	busy_counter_mutex.lock();
    	if (int.parse(message) == 0) {
    		msgid = GLib.Random.int_range(1, int.MAX);
    		busy_messages.add(msgid.to_string()+"|"+message);
    	} else {
    		foreach (string msg in busy_messages) {
    			if (msg.split("|",2)[0] == message) {
    				busy_messages.remove(msg);
    			}
    		
    		}
    	}
    	busy_counter_mutex.unlock();

    	if ( busy_messages.size > 0 ) {
			TrayIcon.setState(TrayIcon.State.Busy);
			Manager.showSpinner( true );
			Manager.showStatus( busy_messages.last().split("|",2)[1] );
    	} else {
			TrayIcon.setState(TrayIcon.State.Normal);
			Manager.showSpinner( false );
			Manager.showStatus( "" );
    	}
			
		return msgid;
    }
    
    public static void on_settings_changed(string key) {
        switch (key) {
        	case "stashes":
        		if (!no_reload) {
	 		        load_stashes();
				    TrayIcon.update();
		        }
			    no_reload = false;
        		break;

        	case "show-trayicon":
				if (settings.get_boolean("show-trayicon")) {
				    TrayIcon.show();
				} else {
				    TrayIcon.hide();
				}
				break;
				
        	case "tray-stash-groups":
				TrayIcon.update();
				break;
        	
        	case "show-stash-groups":
				Manager.show_stash_groups( settings.get_boolean("show-stash-groups") );
				break;
        }
    }
    
    [DBus (name = "com.libertyzero.GnomeEncfsManager")]
    public class DBusService : GLib.Object, DBusInterface {
    	public int _busy_counter;
    	
    	public DBusService() {
    		_busy_counter = 0;
    	}
    
    	public void quit() {
    		GnomeEncfsManager.quit();
    	}
    	
    	public void mount( string dir, string[] envi ) {
    		foreach (Stash s in stashes) {
    			if ((s.mountDir == dir) || (s.cryptDir == dir)) {
    				if (!s.isMounted && !MountDialog.queue.contains(s)) {
    					s.scriptenv = envi;
	    				Timeout.add (0, () => {
							MountDialog.mount(s);
	    					return false;
	    				});
						return;
    				}
    				return;
    			}
    		}
    	}
    	
    	public void unmount( string dir, string[] envi ) {
    		foreach (Stash s in stashes) {
    			if ((s.mountDir == dir) || (s.cryptDir == dir)) {
    				if (s.isMounted && !MountDialog.queue.contains(s)) {
    					s.scriptenv = envi;
	    				Timeout.add (0, () => {
							s.unmount();
	    					return false;
	    				});
						return;
    				}
    				return;
    			}
    		}
    	}
    	
    	public string list_stashes() {
    		string output = "";
    		foreach (Stash s in stashes) {
    			output += s.mountDir + " » " + s.cryptDir + "\n";
    		}
    		return output;
    	}
    	
    	public bool is_mounted( string dir ) {
    		foreach (Stash s in stashes) {
    			if ((s.mountDir == dir) || (s.cryptDir == dir)) {
    				return s.isMounted;
    			}
    		}
    		return false;
    	}
    	
    	public bool is_available( string dir ) {
    		foreach (Stash s in stashes) {
    			if ((s.mountDir == dir) || (s.cryptDir == dir)) {
    				return s.isAvailable;
    			}
    		}
    		return false;
    	}
    	
    	public bool is_configured( string dir ) {
    		foreach (Stash s in stashes) {
    			if ((s.mountDir == dir) || (s.cryptDir == dir)) {
    				return true;
    			}
    		}
    		return false;
    	}
    	
    	public bool remove_stash( string dir ) {
    		foreach (Stash stash in stashes) {
    			if ((stash.mountDir == dir) || (stash.cryptDir == dir)) {
					Secret.password_clear_sync(Secret.get_schema(Secret.SchemaType.COMPAT_NETWORK), null, "server", stash.cryptDir);
		            stashes.remove(stash);
		            stash.destroy();
		            save_stashes();
				    TrayIcon.update();
				    Manager.update();
				    return true;
    			}
    		}
    		return false;
    	}
    	
    	public bool delete_stash( string dir ) {
    		foreach (Stash stash in stashes) {
    			if ((stash.mountDir == dir) || (stash.cryptDir == dir)) {
		            if (stash.isMounted) {
			            stash.unmount();
					}
				
		            string[] argv = new string[3];
		            argv[0] = "rm";
		            argv[1] = "-rf";
		            argv[2] = stash.cryptDir;
		            string error_msg;
		            
		            try {
		                Process.spawn_sync( null, // working directory
		                                    argv,
		                                    null, // environment
		                                    SpawnFlags.SEARCH_PATH,
		                                    null, // spawnchild setup
		                                    null,
		                                    out error_msg,
		                                    null );
		            } catch (GLib.Error e) {
		                GLib.stderr.printf(e.message+"\n");
		            }

		            if (FileUtils.test(stash.cryptDir, FileTest.EXISTS)) {
		                // Error deleting stash
		                return false;
		            }
		            
		            Secret.password_clear_sync(Secret.get_schema(Secret.SchemaType.COMPAT_NETWORK), null, "server", stash.cryptDir);
		            stash.destroy();
		            
				    TreeIter iter;

				    stashes.clear();
				    Stash.get_model().get_iter_first(out iter);
				    do {
				        Stash.get_model().get(iter, 4, out stash, -1);
				        stash.iter = iter;
				        stashes.add(stash);
				    } while (Stash.get_model().iter_next(ref iter));
				    
				    TrayIcon.update();
				    Manager.update();
				    save_stashes();
				    return true;
    			}
    		}
    		return false;
    	}
    	
    	public bool set_( string dir, string variable ) {
    		foreach (Stash s in stashes) {
    			if ((s.mountDir == dir) || (s.cryptDir == dir)) {
					string[] args = variable.split("=", 2);
					
					if (args.length != 2) {
						return false;
					}
					SimpleConf conf = new SimpleConf();
					conf.set_string(args[0], args[1]);

					switch (args[0]) {
						case "mount_directory":
							string tmp;
							s.changeMountDir(conf.get_string(args[0]), out tmp);
							save_stashes();
							return true;
					
						case "mount_on_startup":
							s.autoMount = conf.get_bool(args[0]);
							ConfigureStash.update();
							save_stashes();
							return true;
					
						case "mount_when_available":
							s.mountWhenAvailable = conf.get_bool(args[0]);
							ConfigureStash.update();
							save_stashes();
							return true;
					
						case "allow_other":
							s.allowOther = conf.get_bool(args[0]);
							ConfigureStash.update();
							save_stashes();
							return true;
							
						case "idle_unmount":
							s.unmountWhenIdle = conf.get_bool(args[0]);
							ConfigureStash.update();
							save_stashes();
							return true;
					
						case "idle_unmount_time":
							s.idleTimeout = conf.get_int(args[0]);
							ConfigureStash.update();
							save_stashes();
							return true;
					
						case "use_keyring":
							s.useKeyring = conf.get_bool(args[0]);
							ConfigureStash.update();
							save_stashes();
							return true;
							
						case "show_in_tray":
							s.showInTrayMenu = conf.get_bool(args[0]);
							ConfigureStash.update();
							TrayIcon.update();
							save_stashes();
							return true;
							
						case "encfs_config_file":
							s.encfs6config = conf.get_string(args[0]);
							save_stashes();
							s.update();
	                        Manager.update();
							ConfigureStash.update();
							TrayIcon.update();
							return true;

						case "keep_mount_directory":
                        case "open_file_manager":
						    s.options.set_string(args[0], conf.get_string(args[0]));
							save_stashes();
							s.update();
							ConfigureStash.update();
						    return true;
						    
						default:
							foreach (string opt in ConfigureStashAdvanced.options) {
								if (args[0] == opt) {
									s.options.set_string(args[0], conf.get_string(args[0]));
									s.update();
									save_stashes();
									return true;
								}
							}
							return false;
					}
    			}
    		}
    		return false;
    	}
    	
    	public string get_( string dir, string variable ) {
    		foreach (Stash s in stashes) {
    			if ((s.mountDir == dir) || (s.cryptDir == dir)) {
					switch (variable) {
						case "mount_directory":
							return s.mountDir;
					
						case "mount_on_startup":
							return s.autoMount ? "true" : "false";
					
						case "mount_when_available":
							return s.mountWhenAvailable ? "true" : "false";
					
						case "allow_other":
							return s.allowOther ? "true" : "false";
							
						case "idle_unmount":
							return s.unmountWhenIdle ? "true" : "false";
					
						case "idle_unmount_time":
							return s.idleTimeout.to_string();
					
						case "use_keyring":
							return s.useKeyring ? "true" : "false";
							
						case "show_in_tray":
							return s.showInTrayMenu ? "true" : "false";
							
					    case "encfs_config_file":
					        return s.encfs6config;
					        
					    case "keep_mount_directory":
					    case "open_file_manager":
					        return (s.options.get_string(variable) != "") ? "true" : "false";
							
						default:
							return s.options.get_string(variable);

					}
    			}
    		}
    		return "";
    	}
    	
    	public bool set_pref( string variable ) {
			string[] args = variable.split("=", 2);
			if (args.length != 2) return false;
			bool val = (args[1] == "true");
			SimpleConf conf = new SimpleConf();
			conf.set_string(args[0], args[1]);
			
			switch (args[0]) {
				case "show_trayicon":
					settings.set_boolean("show-trayicon", val);
					return true;
					
				case "start_minimized":
					settings.set_boolean("start-minimized", val);
					return true;
					
				case "unmount_on_logout":
					settings.set_boolean("unmount-on-logout", val);
					return true;
					
				case "mount_dir_base":
					settings.set_string("mount-dir-base", args[1]);
					return true;
					
				case "force_traysystem":
					settings.set_string("force-traysystem", args[1]);
					return true;
					
				case "autostart":
					Preferences.autostart_set_enabled(val);
					return true;
					
				default:
					foreach (string opt in PreferencesAdvanced.options) {
						if (args[0] == opt) {
							options.set_string(args[0], conf.get_string(args[0]));
							return true;
						}
					}
					return false;
			
			}
    	}
    	
    	public string get_pref( string variable ) {
			switch (variable) {
				case "show_trayicon":
					return settings.get_boolean("show-trayicon") ? "true" : "false";
					
				case "start_minimized":
					return settings.get_boolean("start-minimized") ? "true" : "false";
					
				case "unmount_on_logout":
					return settings.get_boolean("unmount-on-logout") ? "true" : "false";
					
				case "mount_dir_base":
					return settings.get_string("mount-dir-base");
					
				case "force_traysystem":
					return settings.get_string("force-traysystem");

				case "autostart":
					return Preferences.autostart_get_enabled() ? "true" : "false";
					
				default:
					return options.get_string(variable);
			
			}
    	}
    	
    	public bool import_stash( string stash_dir, string mount_dir, string config ) {
    		foreach (Stash s in stashes) {
    			if ((s.mountDir == mount_dir) || (s.cryptDir == mount_dir)) {
    				return false;
    			}
    			if ((s.mountDir == stash_dir) || (s.cryptDir == stash_dir)) {
    				return false;
    			}
    		}

		    string encfs_file = "";
            if (config == "") {
            	string[] filenames = settings.get_strv("encfs-config-files");
			    bool found = false;
	            foreach (string filename in filenames) {
	            	if (FileUtils.test( Path.build_filename(stash_dir, filename), FileTest.EXISTS )) {
	            		encfs_file = filename;
	            		found = true;
	            	}
	            }
	            if (!found) {
	            	return false;
	            }
	        } else {
	            encfs_file = config;
	        }
	        
            Stash s = new Stash(stash_dir, mount_dir, encfs_file);
            stashes.add(s);
            TrayIcon.update();
            save_stashes();
    		return true;
    	}
    	
    	public bool create_stash( string stash_dir, string mount_dir, string config ) {
    		foreach (Stash s in stashes) {
    			if ((s.mountDir == mount_dir) || (s.cryptDir == mount_dir)) {
    				return false;
    			}
    			if ((s.mountDir == stash_dir) || (s.cryptDir == stash_dir)) {
    				return false;
    			}
    		}

    		AddStash.show_with_custom_defaults( stash_dir, mount_dir, config );
    		return true;
    	}
    	
		public string indicate( string state ) {
			int ret = busy(state);
			if (ret > 0) {
				return ret.to_string();
			} else {
				return "";
			}
		}
		/*
		public bool wait() {
			while (busy_messages.size > 0) {
			}
			return true;
		}
		
		public int get_busy_counter() {
			return busy_messages.size + MountDialog.queue.size;
		}*/
		
		public bool set_var( string variable ) {
			string[] tmp = variable.split("=", 2);
			if (tmp.length == 2) {
				script_variables[tmp[0]] = tmp[1];
			} else if (tmp.length == 1) {
				script_variables[tmp[0]] = "";
			}
			return true;
		}
		
		public string get_var( string variable ) {
			if (script_variables[variable] != null) {
				return script_variables[variable];
			} else {
				return "";
			}
		}
    	
    }

    [DBus (name = "com.libertyzero.GnomeEncfsManager")]
    public interface DBusInterface : GLib.Object {
    	public abstract void quit() throws IOError;
    	public abstract void mount( string dir, string[] envi ) throws IOError;
    	public abstract void unmount( string dir, string[] envi ) throws IOError;
    	public abstract bool is_mounted( string dir ) throws IOError;
    	public abstract bool is_available( string dir ) throws IOError;
    	public abstract bool set_( string dir, string variable ) throws IOError;
    	public abstract string get_( string dir, string variable ) throws IOError;
    	public abstract bool set_pref( string variable ) throws IOError;
    	public abstract string get_pref( string variable ) throws IOError;
    	public abstract bool create_stash( string mount_dir, string stash_dir, string config ) throws IOError;
    	public abstract bool import_stash( string mount_dir, string stash_dir, string config ) throws IOError;
		public abstract bool remove_stash( string dir ) throws IOError;
		public abstract bool delete_stash( string dir ) throws IOError;
		public abstract bool is_configured( string dir ) throws IOError;
		public abstract string indicate( string state ) throws IOError;
		public abstract string list_stashes() throws IOError;
		
		public abstract bool set_var( string variable ) throws IOError;
		public abstract string get_var( string variable ) throws IOError;
		//public abstract bool wait() throws IOError;
		//public abstract int get_busy_counter() throws IOError;
		//public signal void busy_counter_changed(int counter);
    }
    
    public static bool checkStash(string dir, bool verbose) {
		File stash_dir = File.new_for_path( dir );
		string cmd_output;
		bool ret_val = true;
		
		if (verbose) { GLib.stdout.printf("\nStash directory exists and is a folder ..."); }
		if (stash_dir.query_file_type(FileQueryInfoFlags.NONE, null) == FileType.DIRECTORY) {
			if (verbose) { GLib.stdout.printf("	yes\n"); }
		} else {
			if (verbose) { GLib.stdout.printf("	NO\n"); }
			ret_val = false;
			if (verbose) { 
				//try {
				//Process.spawn_command_line_sync("ls -ld \""+dir+"\"", out cmd_output);
				//GLib.stdout.printf(cmd_output +"\n");
				//} catch (GLib.Error e) {}
			}
			return ret_val;
		}
		if (verbose) { 
			try {
			Process.spawn_command_line_sync("ls -ld \""+dir+"\"", out cmd_output);
			GLib.stdout.printf(cmd_output +"\n");
			} catch (GLib.Error e) {}
		}

		if (verbose) { GLib.stdout.printf("Encfs config file exists ..."); }
        string[] filenames = settings.get_strv("encfs-config-files");
        string encfs_file = "";
		bool found = false;
        foreach (string filename in filenames) {
        	if (FileUtils.test( Path.build_filename(dir, filename), FileTest.EXISTS )) {
        		encfs_file = filename;
        		found = true;
        	}
        }
		if (found) {
			if (verbose) { GLib.stdout.printf("			yes ("+encfs_file+")\n"); }
		} else {
			if (verbose) { GLib.stdout.printf("			NO\n"); }
			ret_val = false;
			return ret_val;
		}
		
		if (verbose) { GLib.stdout.printf("Encfs config file is readable ..."); }
    	string contents = null;
    	try {
        	FileUtils.get_contents(Path.build_filename(dir,encfs_file), out contents);
        	if (verbose) { GLib.stdout.printf("		yes\n"); }

		    
		    if (verbose) { GLib.stdout.printf("Encfs config file looks valid ..."); }
		    if (contents.split("<").length == contents.split(">").length) {
		    	if (verbose) { GLib.stdout.printf("		yes\n"); }
		    } else {
			    if (verbose) { GLib.stdout.printf("		NO\n"); }
			    ret_val = false;
		    }
		    
	    } catch (GLib.Error e) {
            if (verbose) { GLib.stdout.printf("		NO\n"); }
            ret_val = false;
        }
        if (verbose) { 
		    try {
			Process.spawn_command_line_sync("ls -ld \""+Path.build_filename(dir,encfs_file)+"\"", out cmd_output);
			GLib.stdout.printf(cmd_output +"\n");
			} catch (GLib.Error e) {}
			GLib.stdout.printf("\n");
		}
		return ret_val;
    }
    
    public static void printCliHelpText() {
    	GLib.stdout.printf("Version: " + Config.VERSION.split("~")[0] + "\n" +
"""
Options:
  --version			show the version and exit
  --help			show this help and exit
  --reset			Reset all stashes and settings
  --replace			Replace the running instance
  --nostartupmount		don't mount the stashes at startup
  --checkstash <directory>	do some common checks on the
  				stash in <directory>
		
The following commands allow you to control a running instance of GEncfsM.
<directory> can be either the stash directory or the mount directory.

  * list_stashes
  * mount		<directory>
  * unmount		<directory>
  * is_available	<directory>
  * is_configured	<directory>
  * is_mounted		<directory>
  * set			variable=value <directory>
  * get			variable <directory>
  * set_pref		variable=value
  * get_pref		variable
  * set_var		variable=value
  * get_var		variable
  * create_stash	<stash_directory> <mount_directory> [config_file]
  * import_stash	<stash_directory> <mount_directory> [config_file]
  * remove_stash	<directory>
  * delete_stash	<directory>
  * indicate		your_status_message | message_id
  * quit
  
E.g. gnome-encfs-manager set mount_directory="/path/to/new mount dir" :/.Crypt

""");
    }

    public static void processCliFlags(string[] args) {
    	if (args.length > 1) {
		switch (args[1]) {
		case "--version":
			GLib.stdout.printf(Config.VERSION.split("~")[0] +"\n");
			Posix.exit(0);
			break;
			
		case "--help":
			printCliHelpText();
			Posix.exit(0);
			break;

		case "--nostartupmount":
			cliFlagNoStartupMount = true;
			break;
			
		case "--replace":
			cliFlagReplace = true;
			try {
				DBusInterface service = Bus.get_proxy_sync( BusType.SESSION,
													  	  	dbus_name, dbus_path );
				service.quit();
			} catch (IOError e) {
				GLib.stderr.printf("%s\n", e.message);
			}
			break;
			
		case "--reset":
			foreach (string key in settings.list_keys()) {
				settings.reset(key);
			}
			
			Gtk.init( ref args );
            while (Gtk.events_pending())
				Gtk.main_iteration();

			Posix.exit(0);
			break;

		case "--checkstash":
			int ret_val = 0;
			if (args.length == 3) {
				ret_val = checkStash(args[2], true) ? 0 : 1;
			} else {
				GLib.stdout.printf("\ncheckstash: Please provide a stash directory\n\n");
			}
			Posix.exit(ret_val);
			break;
			
		case "quit":
			try {
				//Posix.usleep(dbus_delay);
				//DBusInterface service = Bus.get_proxy_sync( BusType.SESSION,
				//									  	  	dbus_name, dbus_path );
				DBusClient service = new DBusClient();
				service.quit();
			} catch (IOError e) {
				GLib.stderr.printf("%s\n", e.message);
			}
			Posix.exit(0);
			break;
			
		case "set":
			if (args.length < 3) {
				GLib.stdout.printf("set list: show the available variables\n");
				Posix.exit(1);
			}
			
			if (args[2] == "list") {
				GLib.stdout.printf("\n");
				foreach ( string ent in stash_var_list) {
					GLib.stdout.printf("  * %s\n", ent);
				}
				GLib.stdout.printf("\n");
				Posix.exit(0);
			}
			
			if (args.length != 4) {
				GLib.stdout.printf("Usage: set \"variable=value\" <directory>\n");
				Posix.exit(1);
			}
			
			int ret_val = 1;
			try {
				//Posix.usleep(dbus_delay);
				//DBusInterface service = Bus.get_proxy_sync( BusType.SESSION,
				//									  	  	dbus_name, dbus_path );
				
				DBusClient service = new DBusClient();
				ret_val = service.set_( abs_dir(args[3]), args[2] ) ? 0 : 1;
			} catch (IOError e) {
				GLib.stderr.printf("%s\n", e.message);
			}
			Posix.exit(ret_val);
			break;
			
		case "set_var":
			if (args.length != 3) {
				GLib.stdout.printf("Usage: set_var variable=value\n");
				Posix.exit(1);
			}

			try {
				//Posix.usleep(dbus_delay);
				//DBusInterface service = Bus.get_proxy_sync( BusType.SESSION,
				//									  	  	dbus_name, dbus_path );
				
				DBusClient service = new DBusClient();
				service.set_var( args[2] );
			} catch (IOError e) {
				GLib.stderr.printf("%s\n", e.message);
			}
			Posix.exit(0);
			break;
			
		case "get_var":
			if (args.length != 3) {
				GLib.stdout.printf("Usage: get_var variable\n");
				Posix.exit(1);
			}

			try {
				//Posix.usleep(dbus_delay);
				//DBusInterface service = Bus.get_proxy_sync( BusType.SESSION,
				//									  	  	dbus_name, dbus_path );
				
				DBusClient service = new DBusClient();
				GLib.stdout.printf(service.get_var( args[2] ));
			} catch (IOError e) {
				GLib.stderr.printf("%s\n", e.message);
			}
			Posix.exit(0);
			break;
			
		case "set_pref":
			if (args.length < 3) {
				GLib.stdout.printf("set_pref list: show the available variables\n");
				Posix.exit(1);
			}
			
			if (args.length != 3) {
				GLib.stdout.printf("Usage: set_pref \"variable=value\"\n");
				Posix.exit(1);
			}
			
			if (args[2] == "list") {
				GLib.stdout.printf("\n");
				foreach ( string ent in pref_var_list) {
					GLib.stdout.printf("  * %s\n", ent);
				}
				GLib.stdout.printf("\n");
				Posix.exit(0);
			}

			int ret_val = 1;
			try {
				//Posix.usleep(dbus_delay);
				//DBusInterface service = Bus.get_proxy_sync( BusType.SESSION,
				//									  	  	dbus_name, dbus_path );
				
				DBusClient service = new DBusClient();
				ret_val = service.set_pref( args[2] ) ? 0 : 1;
			} catch (IOError e) {
				GLib.stderr.printf("%s\n", e.message);
			}
			Posix.exit(ret_val);
			break;
			
		case "create_stash":
			if (args.length < 4) {
				GLib.stdout.printf("Usage: create_stash <stash_directory> <mount_directory> [config_file]\n");
				Posix.exit(1);
			}
			try {
				//Posix.usleep(dbus_delay);
				//DBusInterface service = Bus.get_proxy_sync( BusType.SESSION,
				//									  	  	dbus_name, dbus_path );
				DBusClient service = new DBusClient();
				if (args.length == 4) {
				    service.create_stash( abs_dir(args[2]), abs_dir(args[3]), "" );
				} else if (args.length == 5) {
				    string config = args[4];
		            if (config[0:2] == ":/") {
			            config = Path.build_path(Path.DIR_SEPARATOR_S, Stash.get_mount_dir_base(), config[1:config.length]);
		            }
    				service.create_stash( abs_dir(args[2]), abs_dir(args[3]), config );
				}
			} catch (IOError e) {
				GLib.stderr.printf("%s\n", e.message);
			}
			Posix.exit(0);
			break;
			
		case "import_stash":
			if (args.length < 4) {
				GLib.stdout.printf("Usage: import_stash <stash_directory> <mount_directory> [config_file]\n");
				Posix.exit(1);
			}
			try {
				//Posix.usleep(dbus_delay);
				//DBusInterface service = Bus.get_proxy_sync( BusType.SESSION,
				//									  	  	dbus_name, dbus_path );
				DBusClient service = new DBusClient();
				if (args.length == 4) {
				    service.import_stash( abs_dir(args[2]), abs_dir(args[3]), "" );
				} else if (args.length == 5) {
				    string config = args[4];
		            if (config[0:2] == ":/") {
			            config = Path.build_path(Path.DIR_SEPARATOR_S, Stash.get_mount_dir_base(), config[1:config.length]);
		            }
    				service.import_stash( abs_dir(args[2]), abs_dir(args[3]), config );
				}
			} catch (IOError e) {
				GLib.stderr.printf("%s\n", e.message);
			}
			Posix.exit(0);
			break;
			
			
		case "remove_stash":
			if (args.length != 3) {
				GLib.stdout.printf("Usage: remove_stash <directory>\n");
				Posix.exit(1);
			}
			try {
				//Posix.usleep(dbus_delay);
				//DBusInterface service = Bus.get_proxy_sync( BusType.SESSION,
				//									  	  	dbus_name, dbus_path );
				DBusClient service = new DBusClient();
				service.remove_stash( abs_dir(args[2]) );
			} catch (IOError e) {
				GLib.stderr.printf("%s\n", e.message);
			}
			Posix.exit(0);
			break;
			
		case "delete_stash":
			if (args.length != 3) {
				GLib.stdout.printf("Usage: delete_stash <directory>\n");
				Posix.exit(1);
			}
			try {
				//Posix.usleep(dbus_delay);
				//DBusInterface service = Bus.get_proxy_sync( BusType.SESSION,
				//									  	  	dbus_name, dbus_path );
				DBusClient service = new DBusClient();
				service.delete_stash( abs_dir(args[2]) );
			} catch (IOError e) {
				GLib.stderr.printf("%s\n", e.message);
			}
			Posix.exit(0);
			break;
			
		case "mount":
			if (args.length != 3) {
				GLib.stdout.printf("mount: Please provide the mount or stash directory\n");
				Posix.exit(1);
			}
			try {
				//Posix.usleep(dbus_delay);
				//DBusInterface service = Bus.get_proxy_sync( BusType.SESSION,
				//									  	  	dbus_name, dbus_path );
													  	  	
				int i = 0;
				foreach ( string s in GLib.Environment.list_variables() ) {
					if (s[0:2] == "__") {
						i++;
					}
				}
				string[] envi = new string[i];
				i = 0;
				foreach ( string s in GLib.Environment.list_variables() ) {
					if (s[0:2] == "__") {
						envi[i] = s;
						i++;
					}
				}
				DBusClient service = new DBusClient();
				service.mount( abs_dir(args[2]), envi );
			} catch (IOError e) {
				GLib.stderr.printf("%s\n", e.message);
			}
			Posix.exit(0);
			break;
			
		case "unmount":
			if (args.length != 3) {
				GLib.stdout.printf("unmount: Please provide the mount or stash directory\n");
				Posix.exit(1);
			}
			try {
				//Posix.usleep(dbus_delay);
				//DBusInterface service = Bus.get_proxy_sync( BusType.SESSION,
				//									  	  	dbus_name, dbus_path );
													  	  	
				int i = 0;
				foreach ( string s in GLib.Environment.list_variables() ) {
					if (s[0:2] == "__") {
						i++;
					}
				}
				string[] envi = new string[i];
				i = 0;
				foreach ( string s in GLib.Environment.list_variables() ) {
					if (s[0:2] == "__") {
						envi[i] = s;
						i++;
					}
				}
				DBusClient service = new DBusClient();
				service.unmount( abs_dir(args[2]), envi );
			} catch (IOError e) {
				GLib.stderr.printf("%s\n", e.message);
			}
			Posix.exit(0);
			break;
			
		case "is_mounted":
			if (args.length != 3) {
				GLib.stdout.printf("is_mounted: Please provide the mount or stash directory\n");
				Posix.exit(1);
			}
			try {
				//Posix.usleep(dbus_delay);
				//DBusInterface service = Bus.get_proxy_sync( BusType.SESSION,
				//									  	  	dbus_name, dbus_path );
				DBusClient service = new DBusClient();
				if (service.is_mounted( abs_dir(args[2]) ) == true) {
					GLib.stdout.printf("yes\n");
					Posix.exit(0);
				} else {
					GLib.stdout.printf("no\n");
					Posix.exit(1);
				}
			} catch (IOError e) {
				GLib.stderr.printf("%s\n", e.message);
			}
			Posix.exit(1);
			break;
			
		case "is_available":
			if (args.length != 3) {
				GLib.stdout.printf("is_available: Please provide the mount or stash directory\n");
				Posix.exit(1);
			}
			try {
				//Posix.usleep(dbus_delay);
				//DBusInterface service = Bus.get_proxy_sync( BusType.SESSION,
				//									  	  	dbus_name, dbus_path );
				DBusClient service = new DBusClient();
				if (service.is_available( abs_dir(args[2]) ) == true) {
					GLib.stdout.printf("yes\n");
					Posix.exit(0);
				} else {
					GLib.stdout.printf("no\n");
					Posix.exit(1);
				}
			} catch (IOError e) {
				GLib.stderr.printf("%s\n", e.message);
			}
			Posix.exit(1);
			break;
		
		case "is_configured":
			if (args.length != 3) {
				GLib.stdout.printf("is_configured: Please provide the mount or stash directory\n");
				Posix.exit(1);
			}
			try {
				//Posix.usleep(dbus_delay);
				//DBusInterface service = Bus.get_proxy_sync( BusType.SESSION,
				//									  	  	dbus_name, dbus_path );
				DBusClient service = new DBusClient();
				if (service.is_configured( abs_dir(args[2]) ) == true) {
					GLib.stdout.printf("yes\n");
					Posix.exit(0);
				} else {
					GLib.stdout.printf("no\n");
					Posix.exit(1);
				}
			} catch (IOError e) {
				GLib.stderr.printf("%s\n", e.message);
			}
			Posix.exit(1);
			break;
			
		case "indicate":
			if (args.length != 3) {
				GLib.stdout.printf("indicate: available states are idle and busy\n");
				Posix.exit(1);
			}
			try {
				//Posix.usleep(dbus_delay);
				//DBusInterface service = Bus.get_proxy_sync( BusType.SESSION,
				//									  	  	dbus_name, dbus_path );

				DBusClient service = new DBusClient();
				GLib.stdout.puts(service.indicate( args[2] ));
			} catch (IOError e) {
				GLib.stderr.printf("%s\n", e.message);
			}
			Posix.exit(1);
			break;
			
		case "wait":
			try {
				//Posix.usleep(dbus_delay);
				//DBusInterface service = Bus.get_proxy_sync( BusType.SESSION,
				//									  	  	dbus_name, dbus_path );
													  	  	
				/*while (service.get_busy_counter() > 0) {
					service = Bus.get_proxy_sync( BusType.SESSION,
													  	  	dbus_name, dbus_path );
					Posix.usleep(500000);
				}*/

			} catch (IOError e) {
				GLib.stderr.printf("%s\n", e.message);
				GLib.stderr.flush();
			}
			Posix.exit(1);
			break;
			
		case "list_stashes":
			try {
				//Posix.usleep(dbus_delay);
				//DBusInterface service = Bus.get_proxy_sync( BusType.SESSION,
				//									  	  	dbus_name, dbus_path );
				DBusClient service = new DBusClient();
				string result = service.list_stashes();
				GLib.stdout.printf(result);
			} catch (IOError e) {
				GLib.stderr.printf("%s\n", e.message);
			}
			Posix.exit(0);
			break;
			
		case "get":
			if (args.length != 4) {
				GLib.stdout.printf("get: Please provide variable and <directory>\n");
				Posix.exit(1);
			}
			try {
				//Posix.usleep(dbus_delay);
				//DBusInterface service = Bus.get_proxy_sync( BusType.SESSION,
				//									  	  	dbus_name, dbus_path );
				DBusClient service = new DBusClient();
				string result = service.get_( abs_dir(args[3]), args[2] );
				GLib.stdout.printf("%s\n", result);
				Posix.exit( result == "true" ? 0 : 1 );
			} catch (IOError e) {
				GLib.stderr.printf("%s\n", e.message);
			}
			Posix.exit(1);
			break;
			
		case "get_pref":
			if (args.length != 3) {
				GLib.stdout.printf("get_pref: Please provide a variable\n");
				Posix.exit(1);
			}
			try {
				//Posix.usleep(dbus_delay);
				//DBusInterface service = Bus.get_proxy_sync( BusType.SESSION,
				//									  	  	dbus_name, dbus_path );
				DBusClient service = new DBusClient();
				string result = service.get_pref( args[2] );
				GLib.stdout.printf("%s\n", result);
				Posix.exit( result == "true" ? 0 : 1 );
			} catch (IOError e) {
				GLib.stderr.printf("%s\n", e.message);
			}
			Posix.exit(1);
			break;
			
		default:
			printCliHelpText();
			Posix.exit(0);
			break;
			
		}
		}
    }
    
    public static string abs_dir( string dir ) {
    	string new_dir = dir;
		if (new_dir[0:2] == ":/") {
			new_dir = Path.build_path(Path.DIR_SEPARATOR_S, Stash.get_mount_dir_base(), new_dir[1:new_dir.length]);
		}
		if (!Path.is_absolute(new_dir)) {
			new_dir = Path.build_path(Path.DIR_SEPARATOR_S, Environment.get_current_dir(), new_dir);
		}
		if (new_dir[new_dir.length-1] == '/') {
			new_dir = new_dir[0:-1];
		}
		return new_dir;
    }
    
    
    public class DBusClient : GLib.Object {
    	private void void_dbus_send( string method, string args ) {
		    string _stdout = "";
			string _stderr = "";
		    try {
		    Process.spawn_command_line_sync("dbus-send --session --print-reply --type=method_call --dest='%s' '%s' %s.%s %s".printf(dbus_name, dbus_path, dbus_name, method, args), out _stdout, out _stderr);
		    } catch (GLib.Error e) {}
		    GLib.stdout.puts(_stderr);
    	}
    	
    	private bool bool_dbus_send( string method, string args ) {
		    string _stdout = "";
			string _stderr = "";
		    try {
		    Process.spawn_command_line_sync("dbus-send --session --print-reply --type=method_call --dest='%s' '%s' %s.%s %s".printf(dbus_name, dbus_path, dbus_name, method, args), out _stdout, out _stderr);
		    } catch (GLib.Error e) {}
		    GLib.stdout.puts(_stderr);
		    
		    string[] ret =  _stdout.split("\n",2)[1].split(" ", 5);
		    
			if (ret[3] == "boolean") {
				if (ret[4] == "false\n") {
					return false;
				} else {
					return true;
				}
			}
			return true;
    	}
    	
    	private string string_dbus_send( string method, string args ) {
		    string _stdout = "";
			string _stderr = "";
		    try {
		    Process.spawn_command_line_sync("dbus-send --session --print-reply --type=method_call --dest='%s' '%s' %s.%s %s".printf(dbus_name, dbus_path, dbus_name, method, args), out _stdout, out _stderr);
		    } catch (GLib.Error e) {}
		    GLib.stdout.puts(_stderr);
		    
		    string[] ret =  _stdout.split("\n",2)[1].split(" ", 5);

			return ret[4][1:-2];
    	}

    	public bool is_mounted( string dir ) {
    		return bool_dbus_send("IsMounted", "string:'%s'".printf(dir));
    	}
    
    	public bool is_available( string dir ) {
    		return bool_dbus_send("IsAvailable", "string:'%s'".printf(dir));
    	}
    	
    	public bool is_configured( string dir ) {
    		return bool_dbus_send("IsConfigured", "string:'%s'".printf(dir));
    	}
    	
    	public void quit() {
    		void_dbus_send("Quit", "");
    	}
    	
		public string list_stashes() {
			return string_dbus_send("ListStashes", "");
		}
		
		public bool remove_stash( string dir ) {
			return bool_dbus_send("RemoveStash", "string:'%s'".printf(dir));
		}
		
		public bool delete_stash( string dir ) {
			return bool_dbus_send("DeleteStash", "string:'%s'".printf(dir));
		}
		
		public void mount( string dir, string[] envi ) {
		    string args = "string:'%s' array:string:''".printf(dir);
		    foreach (string s in envi) {
		        args += ",\'%s\'".printf(s);
		    }
		    void_dbus_send("Mount", args);
		}
		public void unmount( string dir, string[] envi ) {
		    string args = "string:'%s' array:string:''".printf(dir);
		    foreach (string s in envi) {
		        args += ",\'%s\'".printf(s);
		    }
		    void_dbus_send("Unmount", args);
		}
    	public bool set_( string dir, string variable ) {
    		return bool_dbus_send("Set", "string:'%s' string:'%s'".printf(dir, variable));
    	}
    	public string get_( string dir, string variable ) {
    		return string_dbus_send("Get", "string:'%s' string:'%s'".printf(dir, variable));
    	}
    	public bool set_pref( string variable ) {
    		return bool_dbus_send("SetPref", "string:'%s'".printf(variable));
    	}
    	public string get_pref( string variable ) {
    		return string_dbus_send("GetPref", "string:'%s'".printf(variable));
    	}
    	public bool create_stash( string mount_dir, string stash_dir, string config ) {
    		return bool_dbus_send("CreateStash", "string:'%s' string:'%s' string:'%s'".printf(mount_dir, stash_dir, config));
    	}
    	public bool import_stash( string mount_dir, string stash_dir, string config ) {
    		return bool_dbus_send("ImportStash", "string:'%s' string:'%s' string:'%s'".printf(mount_dir, stash_dir, config));
    	}
		public string indicate(string state) {
			return string_dbus_send("Indicate", "string:'%s'".printf(state));
		}
    	public bool set_var( string variable ) {
    		return bool_dbus_send("SetVar", "string:'%s'".printf(variable));
    	}
    	public string get_var( string variable ) {
    		return string_dbus_send("GetVar", "string:'%s'".printf(variable));
    	}
    }
    
    public delegate void custom_command_callback(int status);
    public static void run_custom_command_async( string variable, string mountDir, custom_command_callback callback ) {
    	string command = options.get_string(variable);
    	
    	if (command == "") {
    		callback(0);
    		return;
    	}

    	ArrayList<string> env = new ArrayList<string> ();
		string[] vars = Environment.list_variables();

		foreach (string xvar in vars) {
			env.add(xvar+"="+Environment.get_variable(xvar));
			command = command.replace("$"+xvar, Environment.get_variable(xvar));
		}
		
    	env.add("MOUNT_DIR="+mountDir);
    	env.add("SCRIPT_TYPE="+variable);
    	foreach (var entry in Stash.scriptStatusCode.entries) {
    		env.add(entry.key+"="+entry.value.to_string());
    	}
		command = command.replace("$MOUNT_DIR", mountDir);
    	
    	GLib.stdout.printf("\n=== Command: %s\n", command);
    	Pid pid;
    	try {
    		string[] argvp;
    		Shell.parse_argv( command, out argvp );
	    	Process.spawn_async(null, argvp, arrayList2Array(env),
	    						SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD,
	    						null, out pid);
	    						
	    	ChildWatch.add(pid, (pid, status) => {
	    		if (callback != null)
	    			callback(Process.exit_status(status));
	    	});
    	} catch (GLib.Error e) {
    		GLib.stderr.printf("%s\n", e.message);
			if (callback != null)
				callback(0);
    	}
    }
    
    public static void quit() {
        on_logout();
        settings.disconnect(sighandler);
        //save_stashes();
        Gtk.main_quit();
    }
    
    public static void on_logout() {
    	if (settings.get_boolean("unmount-on-logout")) {
		    foreach (Stash s in stashes) {
	            s.unmount();
		    }
        }
    }
    
    public static void load_stashes() {
        
        if (stashes == null)
            stashes = new ArrayList<Stash> ();
        else
        	ConfigureStash.kill();
        	MountDialog.kill();
            stashes.clear();
            Stash.model.clear();
            
        Variant v;
        VariantIter iter;
        
        v = settings.get_value("stashes");
        v.get("a(ssbbbbbibsbs)", out iter);

        string encfsDir;        
        string mountDir;
        bool useKeyring;
        bool autoMount;
        bool allowOther;
        bool mountWhenAvailable;
        bool unmountWhenIdle;
        int idleTimeout;
        bool showInTrayMenu;
        bool useUnmountCommand;
        string encfs6config;
        string options;
        
        while (iter.next("(ssbbbbbibsbs)", out encfsDir,
                                           out mountDir,
                                           out useKeyring,
                                           out autoMount,
                                           out allowOther,
                                           out mountWhenAvailable,
                                           out unmountWhenIdle,
                                           out idleTimeout,
                                           out showInTrayMenu,
                                           out encfs6config,
                                           out useUnmountCommand,
                                           out options)) {
            var stash = new Stash(encfsDir, mountDir, encfs6config);
            stash.autoMount = autoMount;
            stash.allowOther = allowOther;
            stash.mountWhenAvailable = mountWhenAvailable;
            stash.unmountWhenIdle = unmountWhenIdle;
            stash.idleTimeout = idleTimeout;
            stash.useKeyring = useKeyring;
            
            stash.showInTrayMenu = showInTrayMenu;
            stash.useUnmountCommand = useUnmountCommand;
            
            stash.options.parse(options);

            stash.after_loading();
            
            stashes.add(stash);
        }
    }
    
    public static void save_stashes() {
		no_reload = true;
        if (stashes.size > 0) {
            VariantBuilder builder = new VariantBuilder(VariantType.ARRAY);
            foreach (Stash s in stashes) {
                builder.add("(ssbbbbbibsbs)",s.cryptDir,
                                             s._mountDir,
                                             s.useKeyring,
                                             s.autoMount,
                                             s.allowOther,
                                             s.mountWhenAvailable,
                                             s.unmountWhenIdle,
                                             s.idleTimeout,
                                             s.showInTrayMenu,
                                             s.encfs6config,
                                             s.useUnmountCommand,
                                             s.options.to_string() );
            }

            settings.set_value("stashes", builder.end());
        }
        else {
        
            //Variant v = new Variant("(ssbbbbbibsbs)");
            settings.set_value("stashes", new Variant("a(ssbbbbbibsbs)"));
        }
    
    }
    
    public static string env_subst(string text) {
		string output = text;
		string[] vars = Environment.list_variables();
		foreach (string s in vars) {
			output = output.replace("$"+s, Environment.get_variable(s));
		}
		return output;	
    }
    
    public static string[] arrayList2Array (ArrayList<string> list) {
        string[] array = new string[list.size];
        int i;
        for (i = 0; i < list.size; i++) {
            array[i] = list.get(i);
        }
        return array;
    }
    
    
	public class SimpleConf {
		private HashMap<string, string> map;

		public SimpleConf() {
			map = new HashMap<string, string> ();
		}
	
		public string to_string() {
			string outp = "";
			foreach (var entry in map.entries) {
				outp += entry.key + ":" + entry.value.length.to_string() + ":" + entry.value;
			}
			return outp;
		}
	
		public bool parse(string text) {
			if (text == "") return false;
			
			int idx_a = 0;
			int idx_b = 0;
			while (idx_b < text.length) {
				idx_a = idx_b;
				idx_b = text.index_of(":", idx_a);
				string variable = text[idx_a:idx_b];
				idx_a = ++idx_b;
				idx_b = text.index_of(":", idx_a);
				int variable_length = int.parse(text[idx_a:idx_b]);
				idx_a = ++idx_b;
				idx_b = idx_a + variable_length;
				map[variable] = text[idx_a:idx_b];
			}
			return false;
		}
	
		public void clear() {
			map.clear();
		}
	
		public void set_int(string key, int val) {
			if (val == 0) {
				map.unset(key, null);
			} else {
				map[key] = val.to_string();
			}
		}
	
		public void set_bool(string key, bool val) {
			if (val == false) {
				map.unset(key, null);
			} else {
				map[key] = val.to_string();
			}
		}
	
		public void set_string(string key, string val) {
			if (val == "") {
				map.unset(key, null);
			} else {
				map[key] = val;
			}
		}
	
		public int get_int(string key) {
			if (map[key] == null)
				return 0;
			return int.parse(map[key]);
		}
	
		public string get_string(string key) {
			if (map[key] == null)
				return "";
			return map[key];
		}

		public bool get_bool(string key) {
			if (map[key] == null)
				return false;

			return map[key] == "true";
		}	
	
	}

}

