Class to represent an entry in the resolver list.
Inheritance: Caliburn.Micro.PropertyChangedBase
		/// <summary>
		///     Refresh the resolver list from the newest csv file.
		/// </summary>
		/// <exception cref="UnauthorizedAccessException"></exception>
		/// <exception cref="NotSupportedException"></exception>
		/// <exception cref="ArgumentOutOfRangeException"></exception>
		/// <exception cref="ArgumentException"></exception>
		/// <exception cref="ArgumentNullException"></exception>
		public async void RefreshResolverListAsync()
        {
			IsRefreshingResolverList = true;
            var state = await DnsCryptProxyListManager.UpdateResolverListAsync().ConfigureAwait(false);
            await Task.Run(() =>
            {
                // we do this, to prevent excessive usage
                Thread.Sleep(2000);
            }).ConfigureAwait(false);
            if (state)
            {
                var proxyList = Path.Combine(Directory.GetCurrentDirectory(),
                    Global.DnsCryptProxyFolder, Global.DnsCryptProxyResolverListName);
                var proxyListSignature = Path.Combine(Directory.GetCurrentDirectory(),
                    Global.DnsCryptProxyFolder, Global.DnsCryptProxySignatureFileName);
                var dnsProxyList =
                    DnsCryptProxyListManager.ReadProxyList(proxyList, proxyListSignature, true);
                if (dnsProxyList != null && dnsProxyList.Any())
                {
					Resolvers.Clear();
                    foreach (var dnsProxy in dnsProxyList)
                    {
                        if (
                            dnsProxy.ProviderPublicKey.Equals(
                                PrimaryDnsCryptProxyManager.DnsCryptProxy.Parameter.ProviderKey))
                        {
                            _primaryResolver = dnsProxy;
							// restore the local port
							_primaryResolver.LocalPort = PrimaryDnsCryptProxyManager.DnsCryptProxy.Parameter.LocalPort;
						}
                        if (
                            dnsProxy.ProviderPublicKey.Equals(
                                SecondaryDnsCryptProxyManager.DnsCryptProxy.Parameter.ProviderKey))
                        {
                            _secondaryResolver = dnsProxy;
                        }
						Resolvers.Add(dnsProxy);
                    }
                }
            }
            else
            {
                _windowManager.ShowMetroMessageBox(
                    LocalizationEx.GetUiString("dialog_message_refresh_failed", Thread.CurrentThread.CurrentCulture),
                    LocalizationEx.GetUiString("dialog_warning_title", Thread.CurrentThread.CurrentCulture),
                    MessageBoxButton.OK, BoxType.Warning);
	            
            }
            IsRefreshingResolverList = false;
        }
        private DnsCryptProxyParameter ConvertProxyEntryToParameter(DnsCryptProxyEntry dnsCryptProxyEntry,
            DnsCryptProxyType dnsCryptProxyType)
        {
            var dnsCryptProxyParameter = new DnsCryptProxyParameter
            {
                ProviderKey = dnsCryptProxyEntry.ProviderPublicKey,
                Plugins = Plugins.ToArray(),
                ProviderName = dnsCryptProxyEntry.ProviderName,
                ResolverAddress = dnsCryptProxyEntry.ResolverAddress,
                ResolverName = dnsCryptProxyEntry.Name,
				LocalPort = dnsCryptProxyEntry.LocalPort,
                ResolversList =
                    Path.Combine(Directory.GetCurrentDirectory(), Global.DnsCryptProxyFolder,
                        Global.DnsCryptProxyResolverListName),
                EphemeralKeys = true,
                TcpOnly = UseTcpOnly
            };

            if (dnsCryptProxyType == DnsCryptProxyType.Primary)
            {
                if (ActAsGlobalGateway)
                {
                    dnsCryptProxyParameter.LocalAddress = Global.GlobalGatewayAddress;
                }
                else
                {
                    dnsCryptProxyParameter.LocalAddress = Global.PrimaryResolverAddress;
                }
            }
            else
            {
                dnsCryptProxyParameter.LocalAddress = Global.SecondaryResolverAddress;
            }

            return dnsCryptProxyParameter;
        }
        private MainViewModel(IWindowManager windowManager, IEventAggregator eventAggregator)
        {
            _windowManager = windowManager;
            eventAggregator.Subscribe(this);

            // automatically use the correct translations if available (fallback: en)
            LocalizeDictionary.Instance.SetCurrentThreadCulture = true;
            LocalizeDictionary.Instance.Culture = Thread.CurrentThread.CurrentCulture;

            // this is already defined in the app.manifest, but to be sure check it again
            if (!IsAdministrator())
            {
                _windowManager.ShowMetroMessageBox(
                    LocalizationEx.GetUiString("dialog_message_bad_privileges", Thread.CurrentThread.CurrentCulture),
                    LocalizationEx.GetUiString("dialog_error_title", Thread.CurrentThread.CurrentCulture),
                    MessageBoxButton.OK, BoxType.Error);
                Environment.Exit(1);
            }

            // do a simple check, if all needed files are available
            if (!ValidateDnsCryptProxyFolder())
            {
                _windowManager.ShowMetroMessageBox(
                    LocalizationEx.GetUiString("dialog_message_missing_proxy_files",
                        Thread.CurrentThread.CurrentCulture),
                    LocalizationEx.GetUiString("dialog_error_title", Thread.CurrentThread.CurrentCulture),
                    MessageBoxButton.OK, BoxType.Error);
                Environment.Exit(1);
            }

            DisplayName = string.Format("{0} {1} ({2})", Global.ApplicationName, VersionUtilities.PublishVersion,
                LocalizationEx.GetUiString("global_ipv6_disabled", Thread.CurrentThread.CurrentCulture));
            _resolvers = new List<DnsCryptProxyEntry>();
			//TODO: make UpdateResolverListOnStart configurable by user
			_updateResolverListOnStart = Global.UpdateResolverListOnStart;
            _isWorkingOnPrimaryService = false;
            _isWorkingOnSecondaryService = false;

            LocalNetworkInterfaces = new CollectionViewSource {Source = _localNetworkInterfaces};
            PrimaryDnsCryptProxyManager = new DnsCryptProxyManager(DnsCryptProxyType.Primary);
            SecondaryDnsCryptProxyManager = new DnsCryptProxyManager(DnsCryptProxyType.Secondary);
            ShowHiddenCards = false;


            if (PrimaryDnsCryptProxyManager.DnsCryptProxy.Parameter.TcpOnly ||
                SecondaryDnsCryptProxyManager.DnsCryptProxy.Parameter.TcpOnly)
            {
                _useTcpOnly = true;
            }

            // check the primary resolver for plugins
            if (PrimaryDnsCryptProxyManager.DnsCryptProxy.Parameter.Plugins.Any())
            {
                _plugins = PrimaryDnsCryptProxyManager.DnsCryptProxy.Parameter.Plugins.ToList();
            }
            else
            {
                if (SecondaryDnsCryptProxyManager.DnsCryptProxy.Parameter.Plugins.Any())
                {
                    _plugins = SecondaryDnsCryptProxyManager.DnsCryptProxy.Parameter.Plugins.ToList();
                }
                else
                {
                    // no stored plugins
                    _plugins = new List<string>();
                }
            }

            var proxyList = Path.Combine(Directory.GetCurrentDirectory(),
                Global.DnsCryptProxyFolder, Global.DnsCryptProxyResolverListName);
            var proxyListSignature = Path.Combine(Directory.GetCurrentDirectory(),
                Global.DnsCryptProxyFolder, Global.DnsCryptProxySignatureFileName);
			if (!File.Exists(proxyList) || !File.Exists(proxyListSignature) || UpdateResolverListOnStart)
            {
				// download and verify the proxy list if there is no one.
				AsyncHelpers.RunSync(DnsCryptProxyListManager.UpdateResolverListAsync);
			}

			var dnsProxyList =
                DnsCryptProxyListManager.ReadProxyList(proxyList, proxyListSignature, true);
            if (dnsProxyList != null && dnsProxyList.Any())
            {
                foreach (var dnsProxy in dnsProxyList)
                {
                    if (
                        dnsProxy.Name.Equals(
                            PrimaryDnsCryptProxyManager.DnsCryptProxy.Parameter.ResolverName))
                    {
                        _primaryResolver = dnsProxy;
						// restore the local port
	                    _primaryResolver.LocalPort = PrimaryDnsCryptProxyManager.DnsCryptProxy.Parameter.LocalPort;
                    }
                    if (
                        dnsProxy.Name.Equals(
                            SecondaryDnsCryptProxyManager.DnsCryptProxy.Parameter.ResolverName))
                    {
                        _secondaryResolver = dnsProxy;
                    }
                    _resolvers.Add(dnsProxy);
                }
            }
            else
            {
                _windowManager.ShowMetroMessageBox(
                    string.Format(
                        LocalizationEx.GetUiString("dialog_message_missing_file",
                            Thread.CurrentThread.CurrentCulture),
                        proxyList, proxyListSignature),
                    LocalizationEx.GetUiString("dialog_error_title", Thread.CurrentThread.CurrentCulture),
                    MessageBoxButton.OK, BoxType.Error);
                Environment.Exit(1);
            }

			// if there is no selected primary resolver, add a default resolver
			if (PrimaryResolver == null)
            {
                var tmpResolver = dnsProxyList.SingleOrDefault(d => d.Name.Equals(Global.DefaultPrimaryResolverName));
                if (tmpResolver == null)
                {
                    tmpResolver =
                        dnsProxyList.SingleOrDefault(d => d.Name.Equals(Global.DefaultPrimaryBackupResolverName));
                }
                PrimaryResolver = tmpResolver;
            }

            // if there is no selected secondary resolver, add a default resolver
            if (SecondaryResolver == null)
            {
                var tmpResolver =
                    dnsProxyList.SingleOrDefault(d => d.Name.Equals(Global.DefaultSecondaryResolverName));
                if (tmpResolver == null)
                {
                    tmpResolver =
                        dnsProxyList.SingleOrDefault(d => d.Name.Equals(Global.DefaultSecondaryBackupResolverName));
                }
                SecondaryResolver = tmpResolver;
            }


            if (PrimaryDnsCryptProxyManager.IsDnsCryptProxyInstalled())
            {
                if (PrimaryDnsCryptProxyManager.IsDnsCryptProxyRunning())
                {
                    _isPrimaryResolverRunning = true;
                }
            }

            if (SecondaryDnsCryptProxyManager.IsDnsCryptProxyInstalled())
            {
                if (SecondaryDnsCryptProxyManager.IsDnsCryptProxyRunning())
                {
                    _isSecondaryResolverRunning = true;
                }
            }

            if (
                PrimaryDnsCryptProxyManager.DnsCryptProxy.Parameter.LocalAddress.Equals(
                    Global.GlobalGatewayAddress))
            {
                _actAsGlobalGateway = true;
                _primaryResolverTitle = string.Format("{0} ({1}:{2})",
                    LocalizationEx.GetUiString("default_settings_primary_header",
                        Thread.CurrentThread.CurrentCulture),
                    Global.GlobalGatewayAddress, Global.PrimaryResolverPort);
            }
            else
            {
                _actAsGlobalGateway = false;
				_primaryResolverTitle = string.Format("{0}",
				   LocalizationEx.GetUiString("default_settings_primary_header",
					   Thread.CurrentThread.CurrentCulture));
			}

            _secondaryResolverTitle = string.Format("{0} ({1}:{2})",
                LocalizationEx.GetUiString("default_settings_secondary_header", Thread.CurrentThread.CurrentCulture),
                Global.SecondaryResolverAddress,
                Global.SecondaryResolverPort);

            // check for new version on every application start
            UpdateAsync();
        }
        public static List<DnsCryptProxyEntry> ReadProxyList(string proxyListFile, string proxyListSignature, bool filterIpv6 = true)
        {
            if (!File.Exists(proxyListFile)) return null;
            if (!File.Exists(proxyListSignature)) return null;

            var dnsCryptProxyList = new List<DnsCryptProxyEntry>();

		    var signature = Minisign.LoadSignatureFromFile(proxyListSignature);
		    var publicKey = Minisign.LoadPublicKeyFromString(Global.PublicKey);

		    // only load signed files!
	        if (Minisign.ValidateSignature(proxyListFile, signature, publicKey))
	        {
		        using (var parser = new TextFieldParser(proxyListFile) {HasFieldsEnclosedInQuotes = true})
		        {
			        parser.SetDelimiters(",");
			        while (!parser.EndOfData)
			        {
				        var s = parser.ReadFields();
				        var tmp = new DnsCryptProxyEntry
				        {
					        Name = ClearString(s[0]),
					        FullName = ClearString(s[1]),
					        Description = ClearString(s[2]),
					        Location = ClearString(s[3]),
					        Coordinates = ClearString(s[4]),
					        Url = ClearString(s[5]),
					        Version = s[6],
					        DnssecValidation = (s[7].Equals("yes")),
					        NoLogs = (s[8].Equals("yes")),
					        Namecoin = (s[9].Equals("yes")),
					        ResolverAddress = ClearString(s[10]),
					        ProviderName = ClearString(s[11]),
					        ProviderPublicKey = ClearString(s[12]),
					        ProviderPublicKeyTextRecord = ClearString(s[13]),
					        LocalPort = Global.PrimaryResolverPort //set the default port 
				        };
				        if (!tmp.Description.Equals("Description"))
				        {
					        if (filterIpv6)
					        {
						        if (!tmp.ResolverAddress.StartsWith("["))
						        {
							        dnsCryptProxyList.Add(tmp);
						        }
					        }
					        else
					        {
						        dnsCryptProxyList.Add(tmp);
					        }
				        }
			        }
		        }
	        }
	        return dnsCryptProxyList;
        }