private async void DiscoverResourcesAsync()
        {
            _discoverResourcesCommand.SetCanExecute(false);
            try
            {
                var serverResources = await _serverSettingsViewModel.GetServerResources();

                var calendars    = serverResources.Calendars.Select(c => new CalendarDataViewModel(c)).ToArray();
                var addressBooks = serverResources.AddressBooks.Select(a => new AddressBookDataViewModel(a)).ToArray();
                var taskLists    = serverResources.TaskLists.Select(d => new TaskListDataViewModel(d)).ToArray();

                using (var selectResourcesForm = SelectResourceForm.CreateForFolderAssignment(_optionTasks, ConnectionTests.ResourceType.Calendar, calendars, addressBooks, taskLists))
                {
                    if (selectResourcesForm.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                    {
                        var optionList = new List <OptionsModel>();

                        foreach (var resource in calendars.Where(c => c.SelectedFolder != null))
                        {
                            var options = CreateOptionsWithCategory(resource);
                            _serverSettingsViewModel.SetResourceUrl(options, resource.Model);
                            _optionTasks.ValidateBulkProfile(options, resource.Model.Privileges, resource.Model.OwnerProperties);
                            optionList.Add(options);
                        }

                        foreach (var resource in addressBooks.Where(c => c.SelectedFolder != null))
                        {
                            var options = CreateOptions(resource);
                            _serverSettingsViewModel.SetResourceUrl(options, resource.Model);
                            _optionTasks.ValidateBulkProfile(options, resource.Model.Privileges, null);
                            optionList.Add(options);
                        }

                        foreach (var resource in taskLists.Where(c => c.SelectedFolder != null))
                        {
                            var options = CreateOptions(resource);
                            _serverSettingsViewModel.SetResourceUrl(options, resource.Model);
                            optionList.Add(options);
                        }

                        _parent.RequestAdd(optionList);
                        _parent.RequestRemoval(this);
                    }
                }
            }
            catch (Exception x)
            {
                s_logger.Error("Exception while DiscoverResourcesAsync.", x);
                string message = null;
                for (Exception ex = x; ex != null; ex = ex.InnerException)
                {
                    message += ex.Message + Environment.NewLine;
                }
                MessageBox.Show(message, OptionTasks.ConnectionTestCaption);
            }
            finally
            {
                _discoverResourcesCommand.SetCanExecute(true);
            }
        }
        private async void DiscoverResourcesAsync()
        {
            _discoverResourcesCommand.SetCanExecute(false);
            try
            {
                var serverResources = await _serverSettingsViewModel.GetServerResources();

                var calendars    = serverResources.Calendars.Select(c => new CalendarDataViewModel(c)).ToArray();
                var addressBooks = serverResources.AddressBooks.Select(a => new AddressBookDataViewModel(a)).ToArray();
                var taskLists    = serverResources.TaskLists.Select(d => new TaskListDataViewModel(d)).ToArray();
                if (OnlyAddNewUrls)
                {
                    // Exclude all resourcres that have already been configured
                    var options        = (_parent as OptionsCollectionViewModel).Options;
                    var configuredUrls = new HashSet <String>(options.Select(o => o.Model.CalenderUrl));
                    calendars    = calendars.Where(c => !configuredUrls.Contains(c.Uri.ToString())).ToArray();
                    addressBooks = addressBooks.Where(c => !configuredUrls.Contains(c.Uri.ToString())).ToArray();
                    taskLists    = taskLists.Where(c => !configuredUrls.Contains(c.Id)).ToArray();
                }

                // --- Create folders if requested and required
                if (AutoCreateOutlookFolders)
                {
                    // https://docs.microsoft.com/en-us/visualstudio/vsto/how-to-programmatically-create-a-custom-calendar
                    // https://msdn.microsoft.com/en-us/library/office/ff184655.aspx
                    // Get Outlook's default calendar folder (this is where we create the Kolab folders)
                    GenericComObjectWrapper <Folder> defaultCalendarFolder = new GenericComObjectWrapper <Folder>(Globals.ThisAddIn.Application.Session.GetDefaultFolder(OlDefaultFolders.olFolderCalendar) as Folder);
                    // Find all Kolab calendars that are not yet synced to an outlook folder
                    foreach (var resource in calendars.Where(c => c.SelectedFolder == null))
                    {
                        string newCalendarName = resource.Name + " (" + Name + ")";
                        GenericComObjectWrapper <Folder> newCalendarFolder = null;
                        try
                        {
                            // Use existing folder if it does exist
                            newCalendarFolder = new GenericComObjectWrapper <Folder>(defaultCalendarFolder.Inner.Folders[newCalendarName] as Folder);
                        }
                        catch
                        {
                            // Create missing folder
                            newCalendarFolder = new GenericComObjectWrapper <Folder>(defaultCalendarFolder.Inner.Folders.Add(newCalendarName, OlDefaultFolders.olFolderCalendar) as Folder);
                            // Make sure it has not been renamed to "name (this computer only)"
                            newCalendarFolder.Inner.Name = newCalendarName;
                        }

                        // use the selected folder for syncing with kolab
                        resource.SelectedFolder = new OutlookFolderDescriptor(newCalendarFolder.Inner.EntryID, newCalendarFolder.Inner.StoreID, newCalendarFolder.Inner.DefaultItemType, newCalendarFolder.Inner.Name, 0);
                    }

                    // Create and assign all Kolab address books that are not yet synced to an outlook folder
                    GenericComObjectWrapper <Folder> defaultAddressBookFolder = new GenericComObjectWrapper <Folder>(Globals.ThisAddIn.Application.Session.GetDefaultFolder(OlDefaultFolders.olFolderContacts) as Folder);
                    foreach (var resource in addressBooks.Where(c => c.SelectedFolder == null))
                    {
                        string newAddressBookName = resource.Name + " (" + Name + ")";
                        GenericComObjectWrapper <Folder> newAddressBookFolder = null;
                        try
                        {
                            newAddressBookFolder = new GenericComObjectWrapper <Folder>(defaultAddressBookFolder.Inner.Folders[newAddressBookName] as Folder);
                        }
                        catch
                        {
                            newAddressBookFolder            = new GenericComObjectWrapper <Folder>(defaultAddressBookFolder.Inner.Folders.Add(newAddressBookName, OlDefaultFolders.olFolderContacts) as Folder);
                            newAddressBookFolder.Inner.Name = newAddressBookName;
                        }

                        resource.SelectedFolder = new OutlookFolderDescriptor(newAddressBookFolder.Inner.EntryID, newAddressBookFolder.Inner.StoreID, newAddressBookFolder.Inner.DefaultItemType, newAddressBookFolder.Inner.Name, 0);
                    }

                    // Create and assign all Kolab address books that are not yet synced to an outlook folder
                    GenericComObjectWrapper <Folder> defaultTaskListsFolder = new GenericComObjectWrapper <Folder>(Globals.ThisAddIn.Application.Session.GetDefaultFolder(OlDefaultFolders.olFolderTasks) as Folder);
                    foreach (var resource in taskLists.Where(c => c.SelectedFolder == null))
                    {
                        string newTaskListName = resource.Name + " (" + Name + ")";
                        GenericComObjectWrapper <Folder> newTaskListFolder = null;
                        try
                        {
                            newTaskListFolder = new GenericComObjectWrapper <Folder>(defaultTaskListsFolder.Inner.Folders[newTaskListName] as Folder);
                        }
                        catch
                        {
                            newTaskListFolder            = new GenericComObjectWrapper <Folder>(defaultTaskListsFolder.Inner.Folders.Add(newTaskListName, OlDefaultFolders.olFolderTasks) as Folder);
                            newTaskListFolder.Inner.Name = newTaskListName;
                        }

                        resource.SelectedFolder = new OutlookFolderDescriptor(newTaskListFolder.Inner.EntryID, newTaskListFolder.Inner.StoreID, newTaskListFolder.Inner.DefaultItemType, newTaskListFolder.Inner.Name, 0);
                    }
                }

                using (var selectResourcesForm = SelectResourceForm.CreateForFolderAssignment(_optionTasks, ConnectionTests.ResourceType.Calendar, calendars, addressBooks, taskLists))
                {
                    if (selectResourcesForm.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                    {
                        var optionList = new List <OptionsModel>();

                        foreach (var resource in calendars.Where(c => c.SelectedFolder != null))
                        {
                            var options = CreateOptions(resource);
                            _serverSettingsViewModel.SetResourceUrl(options, resource.Model);
                            _optionTasks.ValidateBulkProfile(options, resource.Model.Privileges, resource.Model.OwnerProperties);
                            optionList.Add(options);
                        }

                        foreach (var resource in addressBooks.Where(c => c.SelectedFolder != null))
                        {
                            var options = CreateOptions(resource);
                            _serverSettingsViewModel.SetResourceUrl(options, resource.Model);
                            _optionTasks.ValidateBulkProfile(options, resource.Model.Privileges, null);
                            optionList.Add(options);
                        }

                        foreach (var resource in taskLists.Where(c => c.SelectedFolder != null))
                        {
                            var options = CreateOptions(resource);
                            _serverSettingsViewModel.SetResourceUrl(options, resource.Model);
                            optionList.Add(options);
                        }

                        _parent.RequestAdd(optionList);
                        _parent.RequestRemoval(this);
                    }
                }
            }
            catch (Exception x)
            {
                s_logger.Error("Exception while DiscoverResourcesAsync.", x);
                string message = null;
                for (Exception ex = x; ex != null; ex = ex.InnerException)
                {
                    message += ex.Message + Environment.NewLine;
                }
                MessageBox.Show(message, OptionTasks.ConnectionTestCaption);
            }
            finally
            {
                _discoverResourcesCommand.SetCanExecute(true);
            }
        }
        public async Task <ServerResources> DiscoverResourcesAsync()
        {
            _discoverResourcesCommand.SetCanExecute(false);
            ServerResources serverResources = new ServerResources();

            try
            {
                s_logger.Debug("Get CalDAV/CardDAV data from Server");
                serverResources = await _serverSettingsViewModel.GetServerResources();

                if (!serverResources.ContainsResources)
                {
                    return(serverResources);
                }

                var calendars       = serverResources.Calendars.Select(c => new CalendarDataViewModel(c)).ToArray();
                var addressBooks    = serverResources.AddressBooks.Select(a => new AddressBookDataViewModel(a)).ToArray();
                var taskLists       = serverResources.TaskLists.Select(d => new TaskListDataViewModel(d)).ToArray();
                var existingOptions = (_parent as OptionsCollectionViewModel).Options;
                if (OnlyAddNewUrls)
                {
                    s_logger.Debug("Exclude all server resources that have already been configured");
                    var configuredUrls = new HashSet <String> (existingOptions.Select(o => o.Model.CalenderUrl));
                    calendars    = calendars.Where(c => !configuredUrls.Contains(c.Uri.ToString())).ToArray();
                    addressBooks = addressBooks.Where(c => !configuredUrls.Contains(c.Uri.ToString())).ToArray();
                    taskLists    = taskLists.Where(c => !configuredUrls.Contains(c.Id)).ToArray();
                }
                // --- Create folders if requested and required
                if (AutoCreateOutlookFolders)
                {
                    s_logger.Debug("Auto-create outlook folders");
                    // https://docs.microsoft.com/en-us/visualstudio/vsto/how-to-programmatically-create-a-custom-calendar
                    // https://msdn.microsoft.com/en-us/library/office/ff184655.aspx
                    // Get Outlook's default calendar folder (this is where we create the Kolab folders)
                    GenericComObjectWrapper <Folder> defaultCalendarFolder = new GenericComObjectWrapper <Folder> (Globals.ThisAddIn.Application.Session.GetDefaultFolder(OlDefaultFolders.olFolderCalendar) as Folder);
                    // Detect wether we have a sync option that syncs the default Outlook calendar
                    bool defaultCalendarFolderIsSynced = existingOptions.Any(o => o.Model.SelectedFolderOrNull?.EntryId == defaultCalendarFolder.Inner.EntryID);

                    // Create and assign all Kolab calendars that are not yet synced to an outlook folder
                    foreach (var resource in calendars.Where(c => c.SelectedFolder == null))
                    {
                        string newCalendarName = resource.Name + " (" + Name + ")";
                        GenericComObjectWrapper <Folder> newCalendarFolder = null;

                        if (resource.Model.IsDefault && !defaultCalendarFolderIsSynced)
                        {
                            s_logger.Info($"Sync Calendar '{newCalendarName}' with default outlook calendar");
                            newCalendarFolder = defaultCalendarFolder;
                        }
                        else
                        {
                            s_logger.Debug($"Find folder for calendar '{newCalendarName}'");
                            newCalendarFolder = findOrCreateFolder(defaultCalendarFolder, newCalendarName, OlDefaultFolders.olFolderCalendar);
                        }
                        // use the selected folder for syncing with kolab
                        if (newCalendarFolder != null)
                        {
                            s_logger.Info($"Use calendar folder '{newCalendarFolder.Inner.Name}' in Sync setting");
                            resource.SelectedFolder = new OutlookFolderDescriptor(newCalendarFolder.Inner.EntryID, newCalendarFolder.Inner.StoreID, newCalendarFolder.Inner.DefaultItemType, newCalendarFolder.Inner.Name, 0);
                        }
                    }

                    // Create and assign all Kolab address books that are not yet synced to an outlook folder
                    GenericComObjectWrapper <Folder> defaultAddressBookFolder = new GenericComObjectWrapper <Folder> (Globals.ThisAddIn.Application.Session.GetDefaultFolder(OlDefaultFolders.olFolderContacts) as Folder);
                    foreach (var resource in addressBooks.Where(c => c.SelectedFolder == null))
                    {
                        string newAddressBookName = resource.Name + " (" + Name + ")";
                        s_logger.Debug($"Find folder for address book '{newAddressBookName}'");
                        GenericComObjectWrapper <Folder> newAddressBookFolder = findOrCreateFolder(defaultAddressBookFolder, newAddressBookName, OlDefaultFolders.olFolderContacts);
                        if (newAddressBookFolder != null)
                        {
                            newAddressBookFolder.Inner.ShowAsOutlookAB = true;
                            // Special handling for GAL delivered by CardDAV: set as default address list
                            if (resource.Uri.Segments.Last() == "ldap-directory/")
                            {
                                var _session = Globals.ThisAddIn.Application.Session;
                                foreach (AddressList ali in _session.AddressLists)
                                {
                                    GenericComObjectWrapper <AddressList> al = new GenericComObjectWrapper <AddressList>(ali);
                                    if (al.Inner.Name == newAddressBookName)
                                    {
                                        // We need to set it in the registry, as there does not seem to exist an appropriate API
                                        // http://www.ericwoodford.com/2016/06/set-default-outlook-address-book-script.html
                                        string regPath =
                                            @"Software\Microsoft\Office\" + Globals.ThisAddIn.Application.Version.Split(new char[] { '.' })[0] + @".0" +
                                            @"\Outlook\Profiles\" + _session.CurrentProfileName +
                                            @"\9207f3e0a3b11019908b08002b2a56c2"; // Outlook default address key
                                        var key = Registry.CurrentUser.OpenSubKey(regPath, true);
                                        if (key != null)
                                        {
                                            s_logger.Info($"Configure LDAP GAL '{newAddressBookName}' as default address book.");
                                            // Turn ID into byte array
                                            byte[] bytes = new byte[al.Inner.ID.Length / 2];
                                            for (int i = 0; i < al.Inner.ID.Length; i += 2)
                                            {
                                                bytes[i / 2] = Convert.ToByte(al.Inner.ID.Substring(i, 2), 16);
                                            }
                                            // Set Outlook default address book subKey
                                            key.SetValue("01023d06", bytes);
                                        }
                                    }
                                }
                            }
                            s_logger.Debug($"Use address book folder '{newAddressBookFolder.Inner.Name}' in Sync setting");
                            resource.SelectedFolder = new OutlookFolderDescriptor(newAddressBookFolder.Inner.EntryID, newAddressBookFolder.Inner.StoreID, newAddressBookFolder.Inner.DefaultItemType, newAddressBookFolder.Inner.Name, 0);
                        }
                    }

                    // Create and assign all Kolab task lists that are not yet synced to an outlook folder
                    GenericComObjectWrapper <Folder> defaultTaskListsFolder = new GenericComObjectWrapper <Folder> (Globals.ThisAddIn.Application.Session.GetDefaultFolder(OlDefaultFolders.olFolderTasks) as Folder);
                    foreach (var resource in taskLists.Where(c => c.SelectedFolder == null))
                    {
                        string newTaskListName = resource.Name + " (" + Name + ")";
                        s_logger.Debug($"Find folder for task list '{newTaskListName}'");
                        GenericComObjectWrapper <Folder> newTaskListFolder = findOrCreateFolder(defaultTaskListsFolder, newTaskListName, OlDefaultFolders.olFolderTasks);
                        if (newTaskListFolder != null)
                        {
                            s_logger.Info($"Use task list folder '{newTaskListFolder.Inner.Name}' in Sync setting");
                            resource.SelectedFolder = new OutlookFolderDescriptor(newTaskListFolder.Inner.EntryID, newTaskListFolder.Inner.StoreID, newTaskListFolder.Inner.DefaultItemType, newTaskListFolder.Inner.Name, 0);
                        }
                    }
                }

                using (var selectResourcesForm = SelectResourceForm.CreateForFolderAssignment(_optionTasks, ConnectionTests.ResourceType.Calendar, calendars, addressBooks, taskLists))
                {
                    // Create and add new sync profiles
                    s_logger.Debug("Create and add all new sync profiles");
                    if (AutoConfigure || selectResourcesForm.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                    {
                        var optionList = new List <OptionsModel>();

                        foreach (var resource in calendars.Where(c => c.SelectedFolder != null))
                        {
                            var options = CreateOptions(resource);
                            _serverSettingsViewModel.SetResourceUrl(options, resource.Model);
                            GenericComObjectWrapper <Folder> folder = new GenericComObjectWrapper <Folder>(
                                Globals.ThisAddIn.Application.Session.GetFolderFromID(resource.SelectedFolder.EntryId) as Folder);
                            if (resource.Model.ReadOnly)
                            {
                                options.SynchronizationMode = SynchronizationMode.ReplicateServerIntoOutlook;
                                folder.Inner.Description    = Strings.Get($"Read-only calendar") + $" »{options.Name}«:\n" + Strings.Get($"local changes made in Outlook are discarded and replaced by data from the server.");
                                try
                                {
                                    // Setting the icon might fail if we sync with the default Outlook calendar folder
                                    folder.Inner.SetCustomIcon(
                                        PictureDispConverter.ToIPictureDisp(Resources.CalendarReadOnly) as stdole.StdPicture);
                                }
                                catch { }
                            }
                            else
                            {
                                options.SynchronizationMode = SynchronizationMode.MergeInBothDirections;
                                folder.Inner.Description    = Strings.Get($"Read-write calendar") + $" »{options.Name}«:\n" + Strings.Get($"local changes made in Outlook and remote changes from the server are merged.");
                                try
                                {
                                    // Setting the icon might fail if we sync with the default Outlook calendar folder
                                    folder.Inner.SetCustomIcon(
                                        PictureDispConverter.ToIPictureDisp(Resources.CalendarReadWrite) as stdole.StdPicture);
                                }
                                catch { }
                            }
                            optionList.Add(options);
                        }

                        foreach (var resource in addressBooks.Where(c => c.SelectedFolder != null))
                        {
                            var options = CreateOptions(resource);
                            _serverSettingsViewModel.SetResourceUrl(options, resource.Model);
                            GenericComObjectWrapper <Folder> folder = new GenericComObjectWrapper <Folder>(
                                Globals.ThisAddIn.Application.Session.GetFolderFromID(resource.SelectedFolder.EntryId) as Folder);
                            if (resource.Model.ReadOnly)
                            {
                                options.SynchronizationMode = SynchronizationMode.ReplicateServerIntoOutlook;
                                folder.Inner.Description    = Strings.Get($"Read-only address book") + $" »{options.Name}«:\n" + Strings.Get($"local changes made in Outlook are discarded and replaced by data from the server.");
                                folder.Inner.SetCustomIcon(
                                    PictureDispConverter.ToIPictureDisp(Resources.AddressbookReadOnly) as stdole.StdPicture);
                            }
                            else
                            {
                                options.SynchronizationMode = SynchronizationMode.MergeInBothDirections;
                                folder.Inner.Description    = Strings.Get($"Read-write address book") + $" »{options.Name}«:\n" + Strings.Get($"local changes made in Outlook and remote changes from the server are merged.");
                                folder.Inner.SetCustomIcon(
                                    PictureDispConverter.ToIPictureDisp(Resources.AddressbookReadWrite) as stdole.StdPicture);
                            }
                            optionList.Add(options);
                        }

                        foreach (var resource in taskLists.Where(c => c.SelectedFolder != null))
                        {
                            var options = CreateOptions(resource);
                            _serverSettingsViewModel.SetResourceUrl(options, resource.Model);
                            GenericComObjectWrapper <Folder> folder = new GenericComObjectWrapper <Folder>(
                                Globals.ThisAddIn.Application.Session.GetFolderFromID(resource.SelectedFolder.EntryId) as Folder);
                            if (resource.Model.ReadOnly)
                            {
                                options.SynchronizationMode = SynchronizationMode.ReplicateServerIntoOutlook;
                                folder.Inner.Description    = Strings.Get($"Read-only task list") + $" »{options.Name}«:\n" + Strings.Get($"local changes made in Outlook are discarded and replaced by data from the server.");
                                folder.Inner.SetCustomIcon(
                                    PictureDispConverter.ToIPictureDisp(Resources.TasklistReadOnly) as stdole.StdPicture);
                            }
                            else
                            {
                                options.SynchronizationMode = SynchronizationMode.MergeInBothDirections;
                                folder.Inner.Description    = Strings.Get($"Read-write task list") + $" »{options.Name}«:\n" + Strings.Get($"local changes made in Outlook and remote changes from the server are merged.");
                                folder.Inner.SetCustomIcon(
                                    PictureDispConverter.ToIPictureDisp(Resources.TasklistReadWrite) as stdole.StdPicture);
                            }
                            optionList.Add(options);
                        }

                        _parent.RequestAdd(optionList);
                        _parent.RequestRemoval(this);
                    }
                }
            }
            catch (Exception x)
            {
                s_logger.Error("Exception while DiscoverResourcesAsync.", x);

                /*string message = null;
                 * for (Exception ex = x; ex != null; ex = ex.InnerException)
                 * message += ex.Message + Environment.NewLine;
                 * MessageBox.Show (message, OptionTasks.ConnectionTestCaption);*/
            }
            finally
            {
                _discoverResourcesCommand.SetCanExecute(true);
            }
            return(serverResources);
        }