diff --git a/WebsitePanel/Database/update_db.sql b/WebsitePanel/Database/update_db.sql index 351f7e6b..1547a587 100644 --- a/WebsitePanel/Database/update_db.sql +++ b/WebsitePanel/Database/update_db.sql @@ -450,6 +450,18 @@ INSERT [dbo].[Providers] ([ProviderID], [GroupID], [ProviderName], [DisplayName] VALUES (1401, 41, N'Lync2013', N'Microsoft Lync Server 2013 Multitenant Hosting Pack', N'WebsitePanel.Providers.HostedSolution.Lync2013, WebsitePanel.Providers.HostedSolution.Lync2013', N'Lync', NULL) END GO + +UPDATE Providers SET DisplayName = N'Microsoft Lync Server 2013 Enterprise Edition' WHERE ProviderID = 1401 +GO + +IF NOT EXISTS (SELECT * FROM [dbo].[Providers] WHERE [ProviderName] = 'Lync2013HP') +BEGIN +INSERT [dbo].[Providers] ([ProviderID], [GroupID], [ProviderName], [DisplayName], [ProviderType], [EditorControl], [DisableAutoDiscovery]) +VALUES (1402, 41, N'Lync2013HP', N'Microsoft Lync Server 2013 Multitenant Hosting Pack', N'WebsitePanel.Providers.HostedSolution.Lync2013HP, WebsitePanel.Providers.HostedSolution.Lync2013HP', N'Lync', NULL) +END +GO + + -- add Application Pools Restart Quota IF NOT EXISTS (SELECT * FROM [dbo].[Quotas] WHERE ([QuotaName] = N'Web.AppPoolsRestart')) diff --git a/WebsitePanel/Lib/References/Microsoft/Lync2013HP/Microsoft.Rtc.Management.Core.dll b/WebsitePanel/Lib/References/Microsoft/Lync2013HP/Microsoft.Rtc.Management.Core.dll new file mode 100644 index 00000000..03bab582 Binary files /dev/null and b/WebsitePanel/Lib/References/Microsoft/Lync2013HP/Microsoft.Rtc.Management.Core.dll differ diff --git a/WebsitePanel/Lib/References/Microsoft/Lync2013HP/Microsoft.Rtc.Management.Hosted.dll b/WebsitePanel/Lib/References/Microsoft/Lync2013HP/Microsoft.Rtc.Management.Hosted.dll new file mode 100644 index 00000000..e7106119 Binary files /dev/null and b/WebsitePanel/Lib/References/Microsoft/Lync2013HP/Microsoft.Rtc.Management.Hosted.dll differ diff --git a/WebsitePanel/Lib/References/Microsoft/Lync2013HP/Microsoft.Rtc.Management.WritableConfig.dll b/WebsitePanel/Lib/References/Microsoft/Lync2013HP/Microsoft.Rtc.Management.WritableConfig.dll new file mode 100644 index 00000000..7979e9ed Binary files /dev/null and b/WebsitePanel/Lib/References/Microsoft/Lync2013HP/Microsoft.Rtc.Management.WritableConfig.dll differ diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.HostedSolution.Lync2013HP/Lync2013HP.cs b/WebsitePanel/Sources/WebsitePanel.Providers.HostedSolution.Lync2013HP/Lync2013HP.cs new file mode 100644 index 00000000..5b50555a --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.HostedSolution.Lync2013HP/Lync2013HP.cs @@ -0,0 +1,1546 @@ +// Copyright (c) 2012, Outercurve Foundation. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// - Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// - Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// - Neither the name of the Outercurve Foundation nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text; +using System.Reflection; +using System.Globalization; +using System.DirectoryServices; + +using Microsoft.Win32; + +using WebsitePanel.Providers; +using WebsitePanel.Providers.HostedSolution; +using WebsitePanel.Providers.Utils; +using WebsitePanel.Server.Utils; + +using System.Management; +using System.Management.Automation; +using System.Management.Automation.Runspaces; + +//using Microsoft.Rtc.Management.Hosted; +using Microsoft.Rtc.Management.WritableConfig.Settings.Edge; +using Microsoft.Rtc.Management.WritableConfig.Settings.SimpleUrl; + + +namespace WebsitePanel.Providers.HostedSolution +{ + public class Lync2013HP : HostingServiceProviderBase, ILyncServer + { + + #region Static constructor + static Lync2013HP() + { + LyncRegistryPath = "SOFTWARE\\Microsoft\\Real-Time Communications"; + } + + public static string LyncRegistryPath + { + get; + set; + } + + #endregion + + + #region Properties + + /// + /// Pool FQDN + /// + private string PoolFQDN + { + get { return ProviderSettings[LyncConstants.PoolFQDN]; } + } + + private string SimpleUrlRoot + { + get { return ProviderSettings[LyncConstants.SimpleUrlRoot]; } + } + + internal string PrimaryDomainController + { + get { return ProviderSettings["PrimaryDomainController"]; } + } + + private string RootOU + { + get { return ProviderSettings["RootOU"]; } + } + + private string RootDomain + { + get { return ServerSettings.ADRootDomain; } + } + + + #endregion + + #region ILyncServer implementation + + public string CreateOrganization(string organizationId, string sipDomain, bool enableConferencing, bool enableConferencingVideo, int maxConferenceSize, bool enabledFederation, bool enabledEnterpriseVoice) + { + return CreateOrganizationInternal(organizationId, sipDomain, enableConferencing, enableConferencingVideo, maxConferenceSize, enabledFederation, enabledEnterpriseVoice); + } + + public bool DeleteOrganization(string organizationId, string sipDomain) + { + return DeleteOrganizationInternal(organizationId, sipDomain); + } + + public bool CreateUser(string organizationId, string userUpn, LyncUserPlan plan) + { + return CreateUserInternal(organizationId, userUpn, plan); + } + + public LyncUser GetLyncUserGeneralSettings(string organizationId, string userUpn) + { + return GetLyncUserGeneralSettingsInternal(organizationId, userUpn); + } + + public bool SetLyncUserGeneralSettings(string organizationId, string userUpn, LyncUser lyncUser) + { + return SetLyncUserGeneralSettingsInternal(organizationId, userUpn, lyncUser); + } + + + public bool SetLyncUserPlan(string organizationId, string userUpn, LyncUserPlan plan) + { + return SetLyncUserPlanInternal(organizationId, userUpn, plan, null); + } + + public bool DeleteUser(string userUpn) + { + return DeleteUserInternal(userUpn); + } + + public LyncFederationDomain[] GetFederationDomains(string organizationId) + { + return GetFederationDomainsInternal(organizationId); + } + + public bool AddFederationDomain(string organizationId, string domainName, string proxyFqdn) + { + return AddFederationDomainInternal(organizationId, domainName, proxyFqdn); + } + + public bool RemoveFederationDomain(string organizationId, string domainName) + { + return RemoveFederationDomainInternal(organizationId, domainName); + } + + public void ReloadConfiguration() + { + ReloadConfigurationInternal(); + } + + public string[] GetPolicyList(LyncPolicyType type, string name) + { + return GetPolicyListInternal(type, name); + } + + #endregion + + #region organization + private string CreateOrganizationInternal(string organizationId, string sipDomain, bool enableConferencing, bool enableConferencingVideo, int maxConferenceSize, bool enabledFederation, bool enabledEnterpriseVoice) + { + sipDomain = sipDomain.ToLower(); + HostedSolutionLog.LogStart("CreateOrganizationInternal"); + HostedSolutionLog.DebugInfo("organizationId: {0}", organizationId); + HostedSolutionLog.DebugInfo("sipDomain: {0}", sipDomain); + + string TenantId = string.Empty; + + LyncTransaction transaction = StartTransaction(); + + Runspace runSpace = null; + try + { + runSpace = OpenRunspace(); + + // create sip domain + Command cmd = new Command("New-CsSipDomain"); + cmd.Parameters.Add("Identity", sipDomain); + ExecuteShellCommand(runSpace, cmd, false); + + transaction.RegisterNewSipDomain(sipDomain); + + //set the msRTCSIP-Domains, TenantID, ObjectID + Guid id = Guid.NewGuid(); + + string path = AddADPrefix(GetOrganizationPath(organizationId)); + DirectoryEntry ou = ActiveDirectoryUtils.GetADObject(path); + ActiveDirectoryUtils.SetADObjectPropertyValue(ou, "msRTCSIP-Domains", sipDomain); + ActiveDirectoryUtils.SetADObjectPropertyValue(ou, "msRTCSIP-TenantId", id); + ActiveDirectoryUtils.SetADObjectPropertyValue(ou, "msRTCSIP-ObjectId", id); + ou.CommitChanges(); + + //Create simpleurls + CreateSimpleUrl(runSpace, sipDomain, id); + transaction.RegisterNewSimpleUrl(sipDomain, id.ToString()); + + //create conferencing policy + cmd = new Command("New-CsConferencingPolicy"); + cmd.Parameters.Add("Identity", organizationId); + + cmd.Parameters.Add("MaxMeetingSize", ((maxConferenceSize == -1) | (maxConferenceSize > 250)) ? 250 : maxConferenceSize); + cmd.Parameters.Add("AllowIPVideo", enableConferencingVideo); + ExecuteShellCommand(runSpace, cmd, false); + transaction.RegisterNewConferencingPolicy(organizationId); + + //create external access policy + cmd = new Command("New-CsExternalAccessPolicy"); + cmd.Parameters.Add("Identity", organizationId); + cmd.Parameters.Add("EnableFederationAccess", true); + cmd.Parameters.Add("EnableOutsideAccess", true); + cmd.Parameters.Add("EnablePublicCloudAccess", false); + cmd.Parameters.Add("EnablePublicCloudAudioVideoAccess", false); + ExecuteShellCommand(runSpace, cmd, false); + transaction.RegisterNewCsExternalAccessPolicy(organizationId); + + //Enable for federation + AllowList allowList = new AllowList(); + DomainPattern domain = new DomainPattern(sipDomain); + allowList.AllowedDomain.Add(domain); + + cmd = new Command("Set-CsTenantFederationConfiguration"); + cmd.Parameters.Add("Tenant", id); + cmd.Parameters.Add("AllowFederatedUsers", true); + cmd.Parameters.Add("AllowedDomains", allowList); + ExecuteShellCommand(runSpace, cmd, false); + + //create mobility policy + cmd = new Command("New-CsMobilityPolicy"); + cmd.Parameters.Add("Identity", organizationId + " EnableOutSideVoice"); + cmd.Parameters.Add("EnableMobility", true); + cmd.Parameters.Add("EnableOutsideVoice", true); + ExecuteShellCommand(runSpace, cmd, false); + transaction.RegisterNewCsMobilityPolicy(organizationId + " EnableOutSideVoice"); + + cmd = new Command("New-CsMobilityPolicy"); + cmd.Parameters.Add("Identity", organizationId + " DisableOutSideVoice"); + cmd.Parameters.Add("EnableMobility", true); + cmd.Parameters.Add("EnableOutsideVoice", false); + ExecuteShellCommand(runSpace, cmd, false); + transaction.RegisterNewCsMobilityPolicy(organizationId + " DisableOutSideVoice"); + + cmd = new Command("Invoke-CsManagementStoreReplication"); + ExecuteShellCommand(runSpace, cmd, false); + + + TenantId = id.ToString(); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("CreateOrganizationInternal", ex); + RollbackTransaction(transaction); + throw; + } + finally + { + + CloseRunspace(runSpace); + } + + HostedSolutionLog.LogEnd("CreateOrganizationInternal"); + + return TenantId; + } + + + + private bool DeleteOrganizationInternal(string organizationId, string sipDomain) + { + HostedSolutionLog.LogStart("DeleteOrganizationInternal"); + HostedSolutionLog.DebugInfo("organizationId: {0}", organizationId); + HostedSolutionLog.DebugInfo("sipDomain: {0}", sipDomain); + + bool ret = true; + + Runspace runSpace = null; + try + { + runSpace = OpenRunspace(); + + Command cmd = new Command("Get-CsTenant"); + cmd.Parameters.Add("Identity", GetOrganizationPath(organizationId)); + Collection result = ExecuteShellCommand(runSpace, cmd, false); + if ((result != null) && (result.Count > 0)) + { + Guid tenantId = (Guid)GetPSObjectProperty(result[0], "TenantId"); + + // create sip domain + string path = AddADPrefix(GetOrganizationPath(organizationId)); + DirectoryEntry ou = ActiveDirectoryUtils.GetADObject(path); + string[] sipDs = (string[])ActiveDirectoryUtils.GetADObjectPropertyMultiValue(ou, "msRTCSIP-Domains"); + + foreach (string sipD in sipDs) + DeleteSipDomain(runSpace, sipD); + + //clear the msRTCSIP-Domains, TenantID, ObjectID + ActiveDirectoryUtils.ClearADObjectPropertyValue(ou, "msRTCSIP-Domains"); + ActiveDirectoryUtils.ClearADObjectPropertyValue(ou, "msRTCSIP-TenantId"); + ActiveDirectoryUtils.ClearADObjectPropertyValue(ou, "msRTCSIP-ObjectId"); + ou.CommitChanges(); + + try + { + DeleteConferencingPolicy(runSpace, organizationId); + } + catch (Exception) + { + } + + try + { + DeleteExternalAccessPolicy(runSpace, organizationId); + } + catch (Exception) + { + } + + try + { + DeleteMobilityPolicy(runSpace, organizationId + " EnableOutSideVoice"); + } + catch (Exception) + { + } + + try + { + DeleteMobilityPolicy(runSpace, organizationId + " DisableOutSideVoice"); + } + catch (Exception) + { + } + + try + { + DeleteSimpleUrl(runSpace, sipDomain, tenantId); + } + catch (Exception) + { + } + } + + cmd = new Command("Invoke-CsManagementStoreReplication"); + ExecuteShellCommand(runSpace, cmd, false); + + } + catch (Exception ex) + { + ret = false; + HostedSolutionLog.LogError("DeleteOrganizationInternal", ex); + throw; + } + finally + { + + CloseRunspace(runSpace); + } + HostedSolutionLog.LogEnd("DeleteOrganizationInternal"); + return ret; + } + #endregion + + #region Users + private bool CreateUserInternal(string organizationId, string userUpn, LyncUserPlan plan) + { + HostedSolutionLog.LogStart("CreateUserInternal"); + HostedSolutionLog.DebugInfo("organizationId: {0}", organizationId); + HostedSolutionLog.DebugInfo("userUpn: {0}", userUpn); + + bool ret = true; + Guid tenantId = Guid.Empty; + + LyncTransaction transaction = StartTransaction(); + + Runspace runSpace = null; + try + { + runSpace = OpenRunspace(); + + Command cmd = new Command("Get-CsTenant"); + cmd.Parameters.Add("Identity", GetOrganizationPath(organizationId)); + Collection result = ExecuteShellCommand(runSpace, cmd, false); + if ((result != null) && (result.Count > 0)) + { + tenantId = (Guid)GetPSObjectProperty(result[0], "TenantId"); + + string[] tmp = userUpn.Split('@'); + if (tmp.Length < 2) return false; + + // Get SipDomains and verify existence + bool bSipDomainExists = false; + cmd = new Command("Get-CsSipDomain"); + Collection sipDomains = ExecuteShellCommand(runSpace, cmd, false); + + foreach (PSObject domain in sipDomains) + { + string d = (string)GetPSObjectProperty(domain, "Name"); + if (d.ToLower() == tmp[1].ToLower()) + { + bSipDomainExists = true; + break; + } + } + + string path = string.Empty; + + if (!bSipDomainExists) + { + // Create Sip Domain + cmd = new Command("New-CsSipDomain"); + cmd.Parameters.Add("Identity", tmp[1].ToLower()); + ExecuteShellCommand(runSpace, cmd, false); + + transaction.RegisterNewSipDomain(tmp[1].ToLower()); + + + path = AddADPrefix(GetOrganizationPath(organizationId)); + DirectoryEntry ou = ActiveDirectoryUtils.GetADObject(path); + string[] sipDs = (string[])ActiveDirectoryUtils.GetADObjectPropertyMultiValue(ou, "msRTCSIP-Domains"); + List listSipDs = new List(); + listSipDs.AddRange(sipDs); + listSipDs.Add(tmp[1]); + + ActiveDirectoryUtils.SetADObjectPropertyValue(ou, "msRTCSIP-Domains", listSipDs.ToArray()); + ou.CommitChanges(); + + //Create simpleurls + CreateSimpleUrl(runSpace, tmp[1].ToLower(), tenantId); + transaction.RegisterNewSimpleUrl(tmp[1].ToLower(), tenantId.ToString()); + } + + //enable for lync + cmd = new Command("Enable-CsUser"); + cmd.Parameters.Add("Identity", userUpn); + cmd.Parameters.Add("RegistrarPool", PoolFQDN); + cmd.Parameters.Add("SipAddressType", "UserPrincipalName"); + ExecuteShellCommand(runSpace, cmd); + + transaction.RegisterNewCsUser(userUpn); + + //set groupingID and tenantID + cmd = new Command("Get-CsAdUser"); + cmd.Parameters.Add("Identity", userUpn); + result = ExecuteShellCommand(runSpace, cmd); + + path = AddADPrefix(GetResultObjectDN(result)); + DirectoryEntry user = ActiveDirectoryUtils.GetADObject(path); + ActiveDirectoryUtils.SetADObjectPropertyValue(user, "msRTCSIP-GroupingID", tenantId); + ActiveDirectoryUtils.SetADObjectPropertyValue(user, "msRTCSIP-TenantId", tenantId); + + if (tmp.Length > 0) + { + string Url = SimpleUrlRoot + tmp[1]; + ActiveDirectoryUtils.SetADObjectPropertyValue(user, "msRTCSIP-BaseSimpleUrl", Url.ToLower()); + } + user.CommitChanges(); + + //set-plan + SetLyncUserPlanInternal(organizationId, userUpn, plan, runSpace); + + //initiate addressbook generation + cmd = new Command("Update-CsAddressBook"); + ExecuteShellCommand(runSpace, cmd, false); + + //initiate user database replication + cmd = new Command("Update-CsUserDatabase"); + ExecuteShellCommand(runSpace, cmd, false); + } + else + { + ret = false; + HostedSolutionLog.LogError("Failed to retrieve tenantID", null); + } + } + catch (Exception ex) + { + ret = false; + HostedSolutionLog.LogError("CreateUserInternal", ex); + RollbackTransaction(transaction); + throw; + } + finally + { + + CloseRunspace(runSpace); + } + HostedSolutionLog.LogEnd("CreateUserInternal"); + return ret; + } + + private LyncUser GetLyncUserGeneralSettingsInternal(string organizationId, string userUpn) + { + HostedSolutionLog.LogStart("GetLyncUserGeneralSettingsInternal"); + HostedSolutionLog.DebugInfo("organizationId: {0}", organizationId); + HostedSolutionLog.DebugInfo("userUpn: {0}", userUpn); + + LyncUser lyncUser = new LyncUser(); + Runspace runSpace = null; + try + { + runSpace = OpenRunspace(); + + Command cmd = new Command("Get-CsUser"); + cmd.Parameters.Add("Identity", userUpn); + Collection result = ExecuteShellCommand(runSpace, cmd); + PSObject user = result[0]; + + lyncUser.DisplayName = (string)GetPSObjectProperty(user, "DisplayName"); + lyncUser.SipAddress = (string)GetPSObjectProperty(user, "SipAddress"); + lyncUser.LineUri = (string)GetPSObjectProperty(user, "LineURI"); + + lyncUser.SipAddress = lyncUser.SipAddress.ToLower().Replace("sip:", ""); + lyncUser.LineUri = lyncUser.LineUri.ToLower().Replace("tel:+", ""); + lyncUser.LineUri = lyncUser.LineUri.ToLower().Replace("tel:", ""); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("GetLyncUserGeneralSettingsInternal", ex); + throw; + } + finally + { + CloseRunspace(runSpace); + } + HostedSolutionLog.LogEnd("GetLyncUserGeneralSettingsInternal"); + return lyncUser; + } + + private bool SetLyncUserGeneralSettingsInternal(string organizationId, string userUpn, LyncUser lyncUser) + { + HostedSolutionLog.LogStart("SetLyncUserGeneralSettingsInternal"); + HostedSolutionLog.DebugInfo("organizationId: {0}", organizationId); + HostedSolutionLog.DebugInfo("userUpn: {0}", userUpn); + + bool ret = true; + Runspace runSpace = null; + Guid tenantId = Guid.Empty; + LyncTransaction transaction = StartTransaction(); + + try + { + runSpace = OpenRunspace(); + Command cmd = new Command("Get-CsTenant"); + cmd.Parameters.Add("Identity", GetOrganizationPath(organizationId)); + Collection result = ExecuteShellCommand(runSpace, cmd, false); + if ((result != null) && (result.Count > 0)) + { + tenantId = (Guid)GetPSObjectProperty(result[0], "TenantId"); + + string[] tmp = userUpn.Split('@'); + if (tmp.Length < 2) return false; + + // Get SipDomains and verify existence + bool bSipDomainExists = false; + cmd = new Command("Get-CsSipDomain"); + Collection sipDomains = ExecuteShellCommand(runSpace, cmd, false); + + foreach (PSObject domain in sipDomains) + { + string d = (string)GetPSObjectProperty(domain, "Name"); + if (d.ToLower() == tmp[1].ToLower()) + { + bSipDomainExists = true; + break; + } + } + + string path = string.Empty; + + if (!bSipDomainExists) + { + // Create Sip Domain + cmd = new Command("New-CsSipDomain"); + cmd.Parameters.Add("Identity", tmp[1].ToLower()); + ExecuteShellCommand(runSpace, cmd, false); + + transaction.RegisterNewSipDomain(tmp[1].ToLower()); + + + path = AddADPrefix(GetOrganizationPath(organizationId)); + DirectoryEntry ou = ActiveDirectoryUtils.GetADObject(path); + string[] sipDs = (string[])ActiveDirectoryUtils.GetADObjectPropertyMultiValue(ou, "msRTCSIP-Domains"); + List listSipDs = new List(); + listSipDs.AddRange(sipDs); + listSipDs.Add(tmp[1]); + + ActiveDirectoryUtils.SetADObjectPropertyValue(ou, "msRTCSIP-Domains", listSipDs.ToArray()); + ou.CommitChanges(); + + //Create simpleurls + CreateSimpleUrl(runSpace, tmp[1].ToLower(), tenantId); + transaction.RegisterNewSimpleUrl(tmp[1].ToLower(), tenantId.ToString()); + + path = AddADPrefix(GetResultObjectDN(result)); + DirectoryEntry user = ActiveDirectoryUtils.GetADObject(path); + + if (tmp.Length > 0) + { + string Url = SimpleUrlRoot + tmp[1]; + ActiveDirectoryUtils.SetADObjectPropertyValue(user, "msRTCSIP-BaseSimpleUrl", Url.ToLower()); + } + user.CommitChanges(); + } + } + + cmd = new Command("Set-CsUser"); + cmd.Parameters.Add("Identity", userUpn); + if (!string.IsNullOrEmpty(lyncUser.SipAddress)) cmd.Parameters.Add("SipAddress", "SIP:" + lyncUser.SipAddress); + if (!string.IsNullOrEmpty(lyncUser.LineUri)) cmd.Parameters.Add("LineUri", "TEL:+" + lyncUser.LineUri); + else cmd.Parameters.Add("LineUri", null); + ExecuteShellCommand(runSpace, cmd, false); + + if (!String.IsNullOrEmpty(lyncUser.PIN)) + { + cmd = new Command("Set-CsClientPin"); + cmd.Parameters.Add("Identity", userUpn); + cmd.Parameters.Add("Pin", lyncUser.PIN); + ExecuteShellCommand(runSpace, cmd, false); + } + + + //initiate addressbook generation + cmd = new Command("Update-CsAddressBook"); + ExecuteShellCommand(runSpace, cmd, false); + + //initiate user database replication + cmd = new Command("Update-CsUserDatabase"); + ExecuteShellCommand(runSpace, cmd, false); + + } + catch (Exception ex) + { + ret = false; + HostedSolutionLog.LogError("SetLyncUserGeneralSettingsInternal", ex); + RollbackTransaction(transaction); + } + finally + { + CloseRunspace(runSpace); + } + HostedSolutionLog.LogEnd("SetLyncUserGeneralSettingsInternal"); + return ret; + } + + + + private bool SetLyncUserPlanInternal(string organizationId, string userUpn, LyncUserPlan plan, Runspace runSpace) + { + HostedSolutionLog.LogStart("SetLyncUserPlanInternal"); + HostedSolutionLog.DebugInfo("organizationId: {0}", organizationId); + HostedSolutionLog.DebugInfo("userUpn: {0}", userUpn); + bool ret = true; + bool bCloseRunSpace = false; + + try + { + if (runSpace == null) + { + runSpace = OpenRunspace(); + bCloseRunSpace = true; + } + + // EnterpriseVoice + Command cmd = new Command("Set-CsUser"); + cmd.Parameters.Add("Identity", userUpn); + cmd.Parameters.Add("EnterpriseVoiceEnabled", plan.EnterpriseVoice); + ExecuteShellCommand(runSpace, cmd, false); + + //CsExternalAccessPolicy + cmd = new Command("Grant-CsExternalAccessPolicy"); + cmd.Parameters.Add("Identity", userUpn); + cmd.Parameters.Add("PolicyName", plan.Federation ? organizationId : null); + ExecuteShellCommand(runSpace, cmd); + + //CsConferencingPolicy + cmd = new Command("Grant-CsConferencingPolicy"); + cmd.Parameters.Add("Identity", userUpn); + cmd.Parameters.Add("PolicyName", plan.Federation ? organizationId : null); + ExecuteShellCommand(runSpace, cmd); + + //CsMobilityPolicy + cmd = new Command("Grant-CsMobilityPolicy"); + cmd.Parameters.Add("Identity", userUpn); + if (plan.Mobility) + cmd.Parameters.Add("PolicyName", plan.MobilityEnableOutsideVoice ? organizationId + " EnableOutSideVoice" : organizationId + " DisableOutSideVoice"); + else + cmd.Parameters.Add("PolicyName", null); + ExecuteShellCommand(runSpace, cmd); + + // ArchivePolicy + cmd = new Command("Grant-CsArchivingPolicy"); + cmd.Parameters.Add("Identity", userUpn); + cmd.Parameters.Add("PolicyName", string.IsNullOrEmpty(plan.ArchivePolicy) ? null : plan.ArchivePolicy); + ExecuteShellCommand(runSpace, cmd); + + // DialPlan + cmd = new Command("Grant-CsDialPlan"); + cmd.Parameters.Add("Identity", userUpn); + cmd.Parameters.Add("PolicyName", string.IsNullOrEmpty(plan.TelephonyDialPlanPolicy) ? null : plan.TelephonyDialPlanPolicy); + ExecuteShellCommand(runSpace, cmd); + + // VoicePolicy + cmd = new Command("Grant-CsVoicePolicy"); + cmd.Parameters.Add("Identity", userUpn); + cmd.Parameters.Add("PolicyName", string.IsNullOrEmpty(plan.TelephonyVoicePolicy) ? null : plan.TelephonyVoicePolicy); + ExecuteShellCommand(runSpace, cmd); + + //initiate user database replication + cmd = new Command("Update-CsUserDatabase"); + ExecuteShellCommand(runSpace, cmd, false); + } + catch (Exception ex) + { + ret = false; + HostedSolutionLog.LogError("SetLyncUserPlanInternal", ex); + throw; + } + finally + { + + if (bCloseRunSpace) CloseRunspace(runSpace); + } + HostedSolutionLog.LogEnd("SetLyncUserPlanInternal"); + return ret; + } + + + private bool DeleteUserInternal(string userUpn) + { + HostedSolutionLog.LogStart("DeleteUserInternal"); + HostedSolutionLog.DebugInfo("userUpn: {0}", userUpn); + + bool ret = true; + + Runspace runSpace = null; + try + { + runSpace = OpenRunspace(); + + //Delete User + DeleteUser(runSpace, userUpn); + + //Clear groupingID and tenantID + Command cmd = new Command("Get-CsAdUser"); + cmd.Parameters.Add("Identity", userUpn); + Collection result = ExecuteShellCommand(runSpace, cmd); + + string path = AddADPrefix(GetResultObjectDN(result)); + DirectoryEntry user = ActiveDirectoryUtils.GetADObject(path); + ActiveDirectoryUtils.ClearADObjectPropertyValue(user, "msRTCSIP-GroupingID"); + ActiveDirectoryUtils.ClearADObjectPropertyValue(user, "msRTCSIP-TenantId"); + ActiveDirectoryUtils.ClearADObjectPropertyValue(user, "msRTCSIP-BaseSimpleUrl"); + user.CommitChanges(); + + //initiate addressbook generation + cmd = new Command("Update-CsAddressBook"); + ExecuteShellCommand(runSpace, cmd, false); + + //initiate user database replication + cmd = new Command("Update-CsUserDatabase"); + ExecuteShellCommand(runSpace, cmd, false); + } + catch (Exception ex) + { + ret = false; + HostedSolutionLog.LogError("DeleteUserInternal", ex); + throw; + } + finally + { + + CloseRunspace(runSpace); + } + HostedSolutionLog.LogEnd("DeleteUserInternal"); + return ret; + } + + internal void DeleteUser(Runspace runSpace, string userUpn) + { + HostedSolutionLog.LogStart("DeleteUser"); + HostedSolutionLog.DebugInfo("userUpn : {0}", userUpn); + Command cmd = new Command("Disable-CsUser"); + cmd.Parameters.Add("Identity", userUpn); + cmd.Parameters.Add("Confirm", false); + ExecuteShellCommand(runSpace, cmd, false); + HostedSolutionLog.LogEnd("DeleteUser"); + } + + #endregion + + #region SipDomains + internal void DeleteSipDomain(Runspace runSpace, string id) + { + HostedSolutionLog.LogStart("DeleteSipDomain"); + HostedSolutionLog.DebugInfo("SipDomain : {0}", id); + Command cmd = new Command("Remove-CsSipDomain"); + cmd.Parameters.Add("Identity", id); + cmd.Parameters.Add("Confirm", false); + cmd.Parameters.Add("Force", true); + ExecuteShellCommand(runSpace, cmd, false); + HostedSolutionLog.LogEnd("DeleteSipDomain"); + } + + private void CreateSimpleUrl(Runspace runSpace, string sipDomain, Guid id) + { + //Create the simpleUrlEntry + Command cmd = new Command("Get-CsSipDomain"); + Collection sipDomains = ExecuteShellCommand(runSpace, cmd, false); + + IList SimpleUrls = new List(); + + + foreach (PSObject domain in sipDomains) + { + string d = (string)GetPSObjectProperty(domain, "Name"); + string Url = SimpleUrlRoot + d; + + + //Create the simpleUrlEntry + cmd = new Command("New-CsSimpleUrlEntry"); + cmd.Parameters.Add("Url", Url); + Collection simpleUrlEntry = ExecuteShellCommand(runSpace, cmd, false); + + //Create the simpleUrl + cmd = new Command("New-CsSimpleUrl"); + cmd.Parameters.Add("Component", "meet"); + cmd.Parameters.Add("Domain", d); + cmd.Parameters.Add("SimpleUrl", simpleUrlEntry[0]); + cmd.Parameters.Add("ActiveUrl", Url); + Collection simpleUrl = ExecuteShellCommand(runSpace, cmd, false); + + SimpleUrls.Add(simpleUrl[0]); + } + + //PSListModifier + cmd = new Command("Set-CsSimpleUrlConfiguration"); + cmd.Parameters.Add("Identity", "Global"); + cmd.Parameters.Add("Tenant", id); + cmd.Parameters.Add("SimpleUrl", SimpleUrls); + ExecuteShellCommand(runSpace, cmd, false); + } + + + internal void DeleteSimpleUrl(Runspace runSpace, string sipDomain, Guid id) + { + /* + //build the url + string Url = SimpleUrlRoot + sipDomain; + + //Create the simpleUrlEntry + Command cmd = new Command("New-CsSimpleUrlEntry"); + cmd.Parameters.Add("Url", Url); + Collection simpleUrlEntry = ExecuteShellCommand(runSpace, cmd, false); + + //Create the simpleUrl + cmd = new Command("New-CsSimpleUrl"); + cmd.Parameters.Add("Component", "meet"); + cmd.Parameters.Add("Domain", sipDomain); + cmd.Parameters.Add("SimpleUrl", simpleUrlEntry[0]); + cmd.Parameters.Add("ActiveUrl", Url); + Collection simpleUrl = ExecuteShellCommand(runSpace, cmd, false); + + //PSListModifier + + Hashtable properties = new Hashtable(); + properties.Add("Remove", simpleUrl[0]); + + cmd = new Command("Set-CsSimpleUrlConfiguration"); + cmd.Parameters.Add("Identity", "Global"); + cmd.Parameters.Add("Tenant", id); + cmd.Parameters.Add("SimpleUrl", properties); + ExecuteShellCommand(runSpace, cmd, false); + */ + } + + + #endregion + + #region Policies + + internal void DeleteConferencingPolicy(Runspace runSpace, string policyName) + { + HostedSolutionLog.LogStart("DeleteConferencingPolicy"); + HostedSolutionLog.DebugInfo("policyName : {0}", policyName); + Command cmd = new Command("Remove-CsConferencingPolicy"); + cmd.Parameters.Add("Identity", policyName); + cmd.Parameters.Add("Confirm", false); + cmd.Parameters.Add("Force", true); + ExecuteShellCommand(runSpace, cmd, false); + HostedSolutionLog.LogEnd("DeleteConferencingPolicy"); + } + + internal void DeleteExternalAccessPolicy(Runspace runSpace, string policyName) + { + HostedSolutionLog.LogStart("DeleteExternalAccessPolicy"); + HostedSolutionLog.DebugInfo("policyName : {0}", policyName); + Command cmd = new Command("Remove-CsExternalAccessPolicy"); + cmd.Parameters.Add("Identity", policyName); + cmd.Parameters.Add("Confirm", false); + cmd.Parameters.Add("Force", true); + ExecuteShellCommand(runSpace, cmd, false); + HostedSolutionLog.LogEnd("DeleteExternalAccessPolicy"); + } + + internal void DeleteMobilityPolicy(Runspace runSpace, string policyName) + { + HostedSolutionLog.LogStart("DeleteMobilityPolicy"); + HostedSolutionLog.DebugInfo("policyName : {0}", policyName); + Command cmd = new Command("Remove-CsMobilityPolicy"); + cmd.Parameters.Add("Identity", policyName); + cmd.Parameters.Add("Confirm", false); + cmd.Parameters.Add("Force", true); + ExecuteShellCommand(runSpace, cmd, false); + HostedSolutionLog.LogEnd("DeleteMobilityPolicy"); + } + + internal string[] GetPolicyListInternal(LyncPolicyType type, string name) + { + List ret = new List(); + + switch (type) + { + case LyncPolicyType.Archiving: + { + Runspace runSpace = OpenRunspace(); + Command cmd = new Command("Get-CsArchivingPolicy"); + Collection result = ExecuteShellCommand(runSpace, cmd, false); + if ((result != null) && (result.Count > 0)) + { + foreach (PSObject res in result) + { + string Identity = GetPSObjectProperty(res, "Identity").ToString(); + ret.Add(Identity); + } + } + } + break; + case LyncPolicyType.DialPlan: + { + Runspace runSpace = OpenRunspace(); + Command cmd = new Command("Get-CsDialPlan"); + Collection result = ExecuteShellCommand(runSpace, cmd, false); + if ((result != null) && (result.Count > 0)) + { + foreach (PSObject res in result) + { + string Identity = GetPSObjectProperty(res, "Identity").ToString(); + string Description = "" + (string)GetPSObjectProperty(res, "Description"); + if (Description.ToLower().IndexOf(name.ToLower()) == -1) continue; + ret.Add(Identity); + } + + + } + } + break; + case LyncPolicyType.Voice: + { + Runspace runSpace = OpenRunspace(); + Command cmd = new Command("Get-CsVoicePolicy"); + Collection result = ExecuteShellCommand(runSpace, cmd, false); + if ((result != null) && (result.Count > 0)) + { + foreach (PSObject res in result) + { + string Identity = GetPSObjectProperty(res, "Identity").ToString(); + string Description = "" + (string)GetPSObjectProperty(res, "Description"); + if (Description.ToLower().IndexOf(name.ToLower()) == -1) continue; + + ret.Add(Identity); + } + + + } + } + break; + case LyncPolicyType.Pin: + { + Runspace runSpace = OpenRunspace(); + Command cmd = new Command("Get-CsPinPolicy"); + Collection result = ExecuteShellCommand(runSpace, cmd, false); + if ((result != null) && (result.Count > 0)) + { + foreach (PSObject res in result) + { + string Identity = GetPSObjectProperty(res, "Identity").ToString(); + string str = "" + GetPSObjectProperty(res, name); + ret.Add(str); + } + } + } + break; + + } + + + + return ret.ToArray(); + } + + #endregion + + #region Sytsem Related Methods + private void ReloadConfigurationInternal() + { + HostedSolutionLog.LogStart("ReloadConfigurationInternal"); + Runspace runSpace = null; + try + { + runSpace = OpenRunspace(); + + Command cmd = new Command("Enable-CsComputer"); + ExecuteShellCommand(runSpace, cmd, false); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("ReloadConfigurationInternal", ex); + throw; + } + finally + { + + CloseRunspace(runSpace); + } + HostedSolutionLog.LogEnd("ReloadConfigurationInternal"); + } + + + #endregion + + #region Federation Domains + private LyncFederationDomain[] GetFederationDomainsInternal(string organizationId) + { + + HostedSolutionLog.LogStart("GetFederationDomainsInternal"); + HostedSolutionLog.DebugInfo("organizationId: {0}", organizationId); + + + LyncFederationDomain[] domains = null; + + Runspace runSpace = null; + try + { + runSpace = OpenRunspace(); + + Guid tenantId = Guid.Empty; + + domains = GetFederationDomainsInternal(runSpace, organizationId, ref tenantId); + + } + catch (Exception ex) + { + HostedSolutionLog.LogError("GetFederationDomainsInternal", ex); + throw; + } + finally + { + + CloseRunspace(runSpace); + } + HostedSolutionLog.LogEnd("GetFederationDomainsInternal"); + + return domains; + } + + private LyncFederationDomain[] GetFederationDomainsInternal(Runspace runSpace, string organizationId, ref Guid tenantId) + { + //Get TenantID + List domains = null; + Command cmd = new Command("Get-CsTenant"); + cmd.Parameters.Add("Identity", GetOrganizationPath(organizationId)); + Collection result = ExecuteShellCommand(runSpace, cmd, false); + if ((result != null) && (result.Count > 0)) + { + tenantId = (Guid)GetPSObjectProperty(result[0], "TenantId"); + + //Get-CSTenantFederationConfiguration (AllowedDomains) + cmd = new Command("Get-CsTenantFederationConfiguration"); + cmd.Parameters.Add("Tenant", tenantId); + result = ExecuteShellCommand(runSpace, cmd, false); + + if ((result != null) && (result.Count > 0)) + { + domains = new List(); + + if (GetPSObjectProperty(result[0], "AllowedDomains").GetType().ToString() == "Microsoft.Rtc.Management.WritableConfig.Settings.Edge.AllowList") + { + AllowList allowList = (AllowList)GetPSObjectProperty(result[0], "AllowedDomains"); + + foreach (DomainPattern d in allowList.AllowedDomain) + { + LyncFederationDomain domain = new LyncFederationDomain(); + domain.DomainName = d.Domain.ToString(); + domains.Add(domain); + } + } + } + } + + if (domains != null) + return domains.ToArray(); + else + return null; + } + + + private bool AddFederationDomainInternal(string organizationId, string domainName, string proxyFqdn) + { + HostedSolutionLog.LogStart("AddFederationDomainInternal"); + HostedSolutionLog.DebugInfo("organizationId: {0}", organizationId); + HostedSolutionLog.DebugInfo("domainName: {0}", domainName); + + domainName = domainName.ToLower(); + proxyFqdn = proxyFqdn.ToLower(); + + Runspace runSpace = null; + try + { + runSpace = OpenRunspace(); + + Guid tenantId = Guid.Empty; + Command cmd = new Command("Get-CsTenant"); + cmd.Parameters.Add("Identity", GetOrganizationPath(organizationId)); + Collection result = ExecuteShellCommand(runSpace, cmd, false); + if ((result != null) && (result.Count > 0)) + { + tenantId = (Guid)GetPSObjectProperty(result[0], "TenantId"); + + //Get-CSTenantFederationConfiguration (AllowedDomains) + cmd = new Command("Get-CsTenantFederationConfiguration"); + cmd.Parameters.Add("Tenant", tenantId); + result = ExecuteShellCommand(runSpace, cmd, false); + + if ((result != null) && (result.Count > 0)) + { + AllowList allowList = null; + + if (GetPSObjectProperty(result[0], "AllowedDomains").GetType().ToString() == "Microsoft.Rtc.Management.WritableConfig.Settings.Edge.AllowList") + { + allowList = (AllowList)GetPSObjectProperty(result[0], "AllowedDomains"); + DomainPattern domain = new DomainPattern(domainName); + allowList.AllowedDomain.Add(domain); + } + else + { + allowList = new AllowList(); + DomainPattern domain = new DomainPattern(domainName); + allowList.AllowedDomain.Add(domain); + } + + cmd = new Command("Set-CsTenantFederationConfiguration"); + cmd.Parameters.Add("Tenant", tenantId); + cmd.Parameters.Add("AllowedDomains", allowList); + ExecuteShellCommand(runSpace, cmd, false); + } + } + } + catch (Exception ex) + { + HostedSolutionLog.LogError("AddFederationDomainInternal", ex); + throw; + } + finally + { + + CloseRunspace(runSpace); + } + HostedSolutionLog.LogEnd("AddFederationDomainInternal"); + + return true; + } + + private bool RemoveFederationDomainInternal(string organizationId, string domainName) + { + HostedSolutionLog.LogStart("RemoveFederationDomainInternal"); + HostedSolutionLog.DebugInfo("organizationId: {0}", organizationId); + HostedSolutionLog.DebugInfo("domainName: {0}", domainName); + + Runspace runSpace = null; + try + { + runSpace = OpenRunspace(); + + Guid tenantId = Guid.Empty; + Command cmd = new Command("Get-CsTenant"); + cmd.Parameters.Add("Identity", GetOrganizationPath(organizationId)); + Collection result = ExecuteShellCommand(runSpace, cmd, false); + if ((result != null) && (result.Count > 0)) + { + tenantId = (Guid)GetPSObjectProperty(result[0], "TenantId"); + + //Get-CSTenantFederationConfiguration (AllowedDomains) + cmd = new Command("Get-CsTenantFederationConfiguration"); + cmd.Parameters.Add("Tenant", tenantId); + result = ExecuteShellCommand(runSpace, cmd, false); + + if ((result != null) && (result.Count > 0)) + { + AllowList allowList = null; + + if (GetPSObjectProperty(result[0], "AllowedDomains").GetType().ToString() == "Microsoft.Rtc.Management.WritableConfig.Settings.Edge.AllowList") + { + HostedSolutionLog.DebugInfo("Remove DomainName: {0}", domainName); + allowList = (AllowList)GetPSObjectProperty(result[0], "AllowedDomains"); + DomainPattern domain = null; + foreach (DomainPattern d in allowList.AllowedDomain) + { + if (d.Domain.ToLower() == domainName.ToLower()) + { + domain = d; + break; + } + } + if (domain != null) + allowList.AllowedDomain.Remove(domain); + } + + cmd = new Command("Set-CsTenantFederationConfiguration"); + cmd.Parameters.Add("Tenant", tenantId); + cmd.Parameters.Add("AllowedDomains", allowList); + ExecuteShellCommand(runSpace, cmd, false); + } + } + } + catch (Exception ex) + { + HostedSolutionLog.LogError("RemoveFederationDomainInternal", ex); + throw; + } + finally + { + + CloseRunspace(runSpace); + } + HostedSolutionLog.LogEnd("RemoveFederationDomainInternal"); + + return true; + } + + + + + #endregion + + #region PowerShell integration + private static InitialSessionState session = null; + + internal virtual Runspace OpenRunspace() + { + HostedSolutionLog.LogStart("OpenRunspace"); + + if (session == null) + { + session = InitialSessionState.CreateDefault(); + session.ImportPSModule(new string[] { "ActiveDirectory", "Lync", "LyncOnline" }); + } + Runspace runSpace = RunspaceFactory.CreateRunspace(session); + // + runSpace.Open(); + // + runSpace.SessionStateProxy.SetVariable("ConfirmPreference", "none"); + HostedSolutionLog.LogEnd("OpenRunspace"); + return runSpace; + } + + internal void CloseRunspace(Runspace runspace) + { + try + { + if (runspace != null && runspace.RunspaceStateInfo.State == RunspaceState.Opened) + { + runspace.Close(); + } + } + catch (Exception ex) + { + HostedSolutionLog.LogError("Runspace error", ex); + } + } + + internal Collection ExecuteShellCommand(Runspace runSpace, Command cmd) + { + return ExecuteShellCommand(runSpace, cmd, true); + } + + internal Collection ExecuteShellCommand(Runspace runSpace, Command cmd, bool useDomainController) + { + object[] errors; + return ExecuteShellCommand(runSpace, cmd, useDomainController, out errors); + } + + internal Collection ExecuteShellCommand(Runspace runSpace, Command cmd, out object[] errors) + { + return ExecuteShellCommand(runSpace, cmd, true, out errors); + } + + internal Collection ExecuteShellCommand(Runspace runSpace, Command cmd, bool useDomainController, out object[] errors) + { + HostedSolutionLog.LogStart("ExecuteShellCommand"); + List errorList = new List(); + + if (useDomainController) + { + CommandParameter dc = new CommandParameter("DomainController", PrimaryDomainController); + if (!cmd.Parameters.Contains(dc)) + { + cmd.Parameters.Add(dc); + } + } + + HostedSolutionLog.DebugCommand(cmd); + Collection results = null; + // Create a pipeline + Pipeline pipeLine = runSpace.CreatePipeline(); + using (pipeLine) + { + // Add the command + pipeLine.Commands.Add(cmd); + // Execute the pipeline and save the objects returned. + results = pipeLine.Invoke(); + + // Log out any errors in the pipeline execution + // NOTE: These errors are NOT thrown as exceptions! + // Be sure to check this to ensure that no errors + // happened while executing the command. + if (pipeLine.Error != null && pipeLine.Error.Count > 0) + { + foreach (object item in pipeLine.Error.ReadToEnd()) + { + errorList.Add(item); + string errorMessage = string.Format("Invoke error: {0}", item); + HostedSolutionLog.LogWarning(errorMessage); + } + } + } + pipeLine = null; + errors = errorList.ToArray(); + HostedSolutionLog.LogEnd("ExecuteShellCommand"); + return results; + } + + internal object GetPSObjectProperty(PSObject obj, string name) + { + return obj.Members[name].Value; + } + + /// + /// Returns the identity of the object from the shell execution result + /// + /// + /// + internal string GetResultObjectIdentity(Collection result) + { + HostedSolutionLog.LogStart("GetResultObjectIdentity"); + if (result == null) + throw new ArgumentNullException("result", "Execution result is not specified"); + + if (result.Count < 1) + throw new ArgumentException("Execution result is empty", "result"); + + if (result.Count > 1) + throw new ArgumentException("Execution result contains more than one object", "result"); + + PSMemberInfo info = result[0].Members["Identity"]; + if (info == null) + throw new ArgumentException("Execution result does not contain Identity property", "result"); + + string ret = info.Value.ToString(); + HostedSolutionLog.LogEnd("GetResultObjectIdentity"); + return ret; + } + + internal string GetResultObjectDN(Collection result) + { + HostedSolutionLog.LogStart("GetResultObjectDN"); + if (result == null) + throw new ArgumentNullException("result", "Execution result is not specified"); + + if (result.Count < 1) + throw new ArgumentException("Execution result does not contain any object"); + + if (result.Count > 1) + throw new ArgumentException("Execution result contains more than one object"); + + PSMemberInfo info = result[0].Members["DistinguishedName"]; + if (info == null) + throw new ArgumentException("Execution result does not contain DistinguishedName property", "result"); + + string ret = info.Value.ToString(); + HostedSolutionLog.LogEnd("GetResultObjectDN"); + return ret; + } + + + #endregion + + #region Transactions + + internal LyncTransaction StartTransaction() + { + return new LyncTransaction(); + } + + internal void RollbackTransaction(LyncTransaction transaction) + { + HostedSolutionLog.LogStart("RollbackTransaction"); + Runspace runSpace = null; + try + { + runSpace = OpenRunspace(); + + + for (int i = transaction.Actions.Count - 1; i > -1; i--) + { + //reverse order + try + { + RollbackAction(transaction.Actions[i], runSpace); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("Rollback error", ex); + } + } + } + catch (Exception ex) + { + HostedSolutionLog.LogError("Rollback error", ex); + } + finally + { + + CloseRunspace(runSpace); + } + HostedSolutionLog.LogEnd("RollbackTransaction"); + } + + private void RollbackAction(TransactionAction action, Runspace runspace) + { + HostedSolutionLog.LogInfo("Rollback action: {0}", action.ActionType); + switch (action.ActionType) + { + case TransactionAction.TransactionActionTypes.LyncNewSipDomain: + DeleteSipDomain(runspace, action.Id); + break; + //case TransactionAction.TransactionActionTypes.LyncNewSimpleUrl: + //DeleteSimpleUrl(runspace, action.Id); + //break; + case TransactionAction.TransactionActionTypes.LyncNewUser: + DeleteUser(runspace, action.Id); + break; + case TransactionAction.TransactionActionTypes.LyncNewConferencingPolicy: + DeleteConferencingPolicy(runspace, action.Id); + break; + case TransactionAction.TransactionActionTypes.LyncNewExternalAccessPolicy: + DeleteExternalAccessPolicy(runspace, action.Id); + break; + case TransactionAction.TransactionActionTypes.LyncNewMobilityPolicy: + DeleteMobilityPolicy(runspace, action.Id); + break; + } + } + + #endregion + + #region helpers + private string GetOrganizationPath(string organizationId) + { + StringBuilder sb = new StringBuilder(); + // append provider + AppendOUPath(sb, organizationId); + AppendOUPath(sb, RootOU); + AppendDomainPath(sb, RootDomain); + + return sb.ToString(); + } + + private static void AppendOUPath(StringBuilder sb, string ou) + { + if (string.IsNullOrEmpty(ou)) + return; + + string path = ou.Replace("/", "\\"); + string[] parts = path.Split('\\'); + for (int i = parts.Length - 1; i != -1; i--) + sb.Append("OU=").Append(parts[i]).Append(","); + } + + private static void AppendDomainPath(StringBuilder sb, string domain) + { + if (string.IsNullOrEmpty(domain)) + return; + + string[] parts = domain.Split('.'); + for (int i = 0; i < parts.Length; i++) + { + sb.Append("DC=").Append(parts[i]); + + if (i < (parts.Length - 1)) + sb.Append(","); + } + } + + internal string AddADPrefix(string path) + { + string dn = path; + if (!dn.ToUpper().StartsWith("LDAP://")) + { + dn = string.Format("LDAP://{0}/{1}", PrimaryDomainController, dn); + } + return dn; + } + + #endregion + + public override bool IsInstalled() + { + string value = ""; + bool bResult = false; + RegistryKey root = Registry.LocalMachine; + RegistryKey rk = root.OpenSubKey(LyncRegistryPath); + if (rk != null) + { + value = (string)rk.GetValue("ProductVersion", null); + if (value == "4.0.7577.0") + bResult = true; + + rk.Close(); + } + return bResult; + } + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.HostedSolution.Lync2013HP/Properties/AssemblyInfo.cs b/WebsitePanel/Sources/WebsitePanel.Providers.HostedSolution.Lync2013HP/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..d2d1c3ed --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.HostedSolution.Lync2013HP/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("WebsitePanel.Providers.HostedSolution.Lync2013HP")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("WebsitePanel.Providers.HostedSolution.Lync2013HP")] +[assembly: AssemblyCopyright("Copyright © 2013")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("dbed0719-736c-43ff-b172-088d89fa403a")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.HostedSolution.Lync2013HP/WebsitePanel.Providers.HostedSolution.Lync2013HP.csproj b/WebsitePanel/Sources/WebsitePanel.Providers.HostedSolution.Lync2013HP/WebsitePanel.Providers.HostedSolution.Lync2013HP.csproj new file mode 100644 index 00000000..97f93005 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.HostedSolution.Lync2013HP/WebsitePanel.Providers.HostedSolution.Lync2013HP.csproj @@ -0,0 +1,79 @@ + + + + + Debug + AnyCPU + {D92F6235-8E5D-47C1-B96A-A2BE40E17889} + Library + Properties + WebsitePanel.Providers.HostedSolution.Lync2013HP + WebsitePanel.Providers.HostedSolution.Lync2013HP + v4.0 + 512 + + + + true + full + false + ..\WebsitePanel.Server\bin\Lync2013\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\WebsitePanel.Server\bin\Lync2013\ + TRACE + prompt + 4 + + + + ..\..\Lib\References\Microsoft\Lync2013HP\Microsoft.Rtc.Management.Core.dll + False + + + ..\..\Lib\References\Microsoft\Lync2013HP\Microsoft.Rtc.Management.WritableConfig.dll + False + + + + + + False + ..\..\..\..\..\Windows\assembly\GAC_MSIL\System.Management.Automation\1.0.0.0__31bf3856ad364e35\System.Management.Automation.dll + + + + + + + + ..\..\Bin\WebsitePanel.Providers.Base.dll + False + + + ..\WebsitePanel.Server\bin\WebsitePanel.Providers.HostedSolution.dll + False + + + ..\WebsitePanel.Server.Utils\bin\Debug\WebsitePanel.Server.Utils.dll + False + + + + + + + + + \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.HostedSolution/Lync2010.cs b/WebsitePanel/Sources/WebsitePanel.Providers.HostedSolution/Lync2010.cs index 9b91a9fe..6eb6cf54 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.HostedSolution/Lync2010.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.HostedSolution/Lync2010.cs @@ -63,7 +63,7 @@ namespace WebsitePanel.Providers.HostedSolution LyncRegistryPath = "SOFTWARE\\Microsoft\\Real-Time Communications"; } - internal static string LyncRegistryPath + public static string LyncRegistryPath { get; set; diff --git a/WebsitePanel/Sources/WebsitePanel.Server.sln b/WebsitePanel/Sources/WebsitePanel.Server.sln index e7b53b8c..99ad0d8b 100644 --- a/WebsitePanel/Sources/WebsitePanel.Server.sln +++ b/WebsitePanel/Sources/WebsitePanel.Server.sln @@ -138,6 +138,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebsitePanel.Providers.Remo EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebsitePanel.Providers.EnterpriseStorage.Windows2012", "WebsitePanel.Providers.EnterpriseStorage.Windows2012\WebsitePanel.Providers.EnterpriseStorage.Windows2012.csproj", "{D435AB26-3AE1-4AAA-B423-50372B2C16F3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebsitePanel.Providers.HostedSolution.Lync2013HP", "WebsitePanel.Providers.HostedSolution.Lync2013HP\WebsitePanel.Providers.HostedSolution.Lync2013HP.csproj", "{D92F6235-8E5D-47C1-B96A-A2BE40E17889}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -708,6 +710,16 @@ Global {D435AB26-3AE1-4AAA-B423-50372B2C16F3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {D435AB26-3AE1-4AAA-B423-50372B2C16F3}.Release|Mixed Platforms.Build.0 = Release|Any CPU {D435AB26-3AE1-4AAA-B423-50372B2C16F3}.Release|x86.ActiveCfg = Release|Any CPU + {D92F6235-8E5D-47C1-B96A-A2BE40E17889}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D92F6235-8E5D-47C1-B96A-A2BE40E17889}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D92F6235-8E5D-47C1-B96A-A2BE40E17889}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {D92F6235-8E5D-47C1-B96A-A2BE40E17889}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {D92F6235-8E5D-47C1-B96A-A2BE40E17889}.Debug|x86.ActiveCfg = Debug|Any CPU + {D92F6235-8E5D-47C1-B96A-A2BE40E17889}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D92F6235-8E5D-47C1-B96A-A2BE40E17889}.Release|Any CPU.Build.0 = Release|Any CPU + {D92F6235-8E5D-47C1-B96A-A2BE40E17889}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {D92F6235-8E5D-47C1-B96A-A2BE40E17889}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {D92F6235-8E5D-47C1-B96A-A2BE40E17889}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/WebsitePanel/Sources/WebsitePanel.Server/Web.config b/WebsitePanel/Sources/WebsitePanel.Server/Web.config index 3bac0736..1b5facc3 100644 --- a/WebsitePanel/Sources/WebsitePanel.Server/Web.config +++ b/WebsitePanel/Sources/WebsitePanel.Server/Web.config @@ -168,7 +168,7 @@ - + \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ExchangeServer/EnterpriseStorageSpaces.ascx.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ExchangeServer/EnterpriseStorageSpaces.ascx.cs index 9de6ea49..74473803 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ExchangeServer/EnterpriseStorageSpaces.ascx.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ExchangeServer/EnterpriseStorageSpaces.ascx.cs @@ -101,7 +101,7 @@ namespace WebsitePanel.Portal.ExchangeServer } // rebind domains - BindDomainNames(); + //BindDomainNames(); BindStats(); } @@ -132,7 +132,7 @@ namespace WebsitePanel.Portal.ExchangeServer } // rebind domains - BindDomainNames(); + //BindDomainNames(); BindStats(); } @@ -155,12 +155,12 @@ namespace WebsitePanel.Portal.ExchangeServer { messageBox.ShowResultMessage(result); if (BusinessErrorCodes.ERROR_USER_ACCOUNT_DEMO == result) - BindDomainNames(); + //BindDomainNames(); return; } // rebind domains - BindDomainNames(); + //BindDomainNames(); } catch (Exception ex) { diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/Lync/LyncCreateUser.ascx.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/Lync/LyncCreateUser.ascx.cs index 9b0e6655..d10176a3 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/Lync/LyncCreateUser.ascx.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/Lync/LyncCreateUser.ascx.cs @@ -122,7 +122,7 @@ namespace WebsitePanel.Portal.Lync bool enterpriseVoiceQuota = Utils.CheckQouta(Quotas.LYNC_ENTERPRISEVOICE, cntx); string lineUri = ""; - if (enterpriseVoiceQuota) lineUri = ddlPhoneNumber.SelectedItem.Text + ":" + tbPin.Text; + if ((enterpriseVoiceQuota) & (ddlPhoneNumber.Items.Count != 0)) lineUri = ddlPhoneNumber.SelectedItem.Text + ":" + tbPin.Text; //#1 LyncUser lyncUser = ES.Services.Lync.GetLyncUserGeneralSettings(PanelRequest.ItemID, accountId); diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/Lync/LyncEditUser.ascx.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/Lync/LyncEditUser.ascx.cs index b92a3b37..92dd4949 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/Lync/LyncEditUser.ascx.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/Lync/LyncEditUser.ascx.cs @@ -130,7 +130,7 @@ namespace WebsitePanel.Portal.Lync bool enterpriseVoiceQuota = Utils.CheckQouta(Quotas.LYNC_ENTERPRISEVOICE, cntx); string lineUri = ""; - if (enterpriseVoiceQuota) lineUri = ddlPhoneNumber.SelectedItem.Text + ":" + tbPin.Text; + if ((enterpriseVoiceQuota) & (ddlPhoneNumber.Items.Count != 0)) lineUri = ddlPhoneNumber.SelectedItem.Text + ":" + tbPin.Text; LyncUserResult res = ES.Services.Lync.SetUserLyncPlan(PanelRequest.ItemID, PanelRequest.AccountID, Convert.ToInt32(planSelector.planId)); if (res.IsSuccess && res.ErrorCodes.Count == 0) diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebsitePanel.Portal.Modules.csproj b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebsitePanel.Portal.Modules.csproj index aa5410db..8353f1a3 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebsitePanel.Portal.Modules.csproj +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebsitePanel.Portal.Modules.csproj @@ -524,6 +524,13 @@ OrgIdPolicyEditor.ascx + + OrgPolicyEditor.ascx + ASPXCodeBehind + + + OrgPolicyEditor.ascx + PackagePhoneNumbers.ascx ASPXCodeBehind @@ -4039,6 +4046,7 @@ + @@ -5225,8 +5233,8 @@ Designer - - + + Designer