// Copyright (c) 2014, 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.Diagnostics; using System.Collections; using System.Collections.Specialized; using System.Windows.Forms; using System.Collections.Generic; using System.Text; using WebsitePanel.EnterpriseServer; using WebsitePanel.Providers.HostedSolution; using WebsitePanel.Providers; using System.Threading; using System.DirectoryServices; namespace WebsitePanel.Import.Enterprise { public class OrganizationImporter { public const int EXCHANGE_CONTAINER_ERROR = -2790; private Label lblProcess; private ProgressBar progressBar; private ApplicationForm appForm; private Button btnImport; private Thread thread; public OrganizationImporter() { } public void Initialize(string username, ApplicationForm appForm) { this.appForm = appForm; this.lblProcess = appForm.lblMessage; this.progressBar = appForm.progressBar; this.btnImport = appForm.btnStart; try { UserInfo info = UserController.GetUser(username); SecurityContext.SetThreadPrincipal(info); } catch (Exception ex) { ShowError("Unable to authenticate user", ex); Cancel(); } } public void Start() { Log.WriteInfo("Import started"); btnImport.Enabled = false; appForm.ImportStarted = true; thread = new Thread(new ThreadStart(this.StartThread)); thread.Start(); } public void Cancel() { Log.WriteInfo("Import canceled"); appForm.ImportStarted = false; btnImport.Enabled = true; this.lblProcess.Text = string.Empty; this.progressBar.Value = 0; } public void Abort() { Cancel(); AbortThread(); } private void AbortThread() { if (this.thread != null) { if (this.thread.IsAlive) { this.thread.Abort(); } this.thread.Join(); } } public DialogResult ShowError(string text, Exception ex) { Log.WriteError(text, ex); return MessageBox.Show(appForm, text, appForm.Text, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); } /// /// Displays process progress. /// public void StartThread() { this.progressBar.Value = 0; try { this.lblProcess.Text = "Creating import script..."; appForm.Update(); //default actions List actions = GetImportActions(); //process actions for (int i = 0; i < actions.Count; i++) { ImportAction action = actions[i]; this.lblProcess.Text = action.Description; this.progressBar.Value = i * 100 / actions.Count; appForm.Update(); switch (action.ActionType) { case ActionTypes.ImportOrganization: ImportOrganization(); break; case ActionTypes.ImportOrganizationDomain: ImportDomain(action.Name); break; case ActionTypes.ImportMailbox: ImportMailbox(action.DirectoryEntry); break; case ActionTypes.ImportContact: ImportContact(action.DirectoryEntry); break; case ActionTypes.ImportGroup: ImportGroup(action.DirectoryEntry); break; } } this.progressBar.Value = 100; } catch (Exception ex) { if (Utils.IsThreadAbortException(ex)) return; string message = Global.ErrorMessage; if ( string.IsNullOrEmpty(message)) message = "An unexpected error has occurred during import."; ShowError(message, ex); Cancel(); return; } this.lblProcess.Text = string.Empty; DialogResult dialogResult = DialogResult.No; if (Global.HasErrors) { dialogResult = MessageBox.Show(appForm, "Import completed with errors. Would you like to see import log?", appForm.Text, MessageBoxButtons.YesNo, MessageBoxIcon.Warning); } else { dialogResult = MessageBox.Show(appForm, "Import completed successfully. Would you like to see import log?", appForm.Text, MessageBoxButtons.YesNo, MessageBoxIcon.Information); } if (dialogResult == DialogResult.Yes) { Log.ShowLogFile(); } btnImport.Enabled = true; appForm.ImportStarted = false; this.progressBar.Value = 0; } public static bool IsThreadAbortException(Exception ex) { Exception innerException = ex; while (innerException != null) { if (innerException is System.Threading.ThreadAbortException) return true; innerException = innerException.InnerException; } string str = ex.ToString(); return str.Contains("System.Threading.ThreadAbortException"); } private List GetImportActions() { List list = new List(); ImportAction action = null; if (Global.ImportAccountsOnly) { ServiceProviderItem item = PackageController.GetPackageItemByName(Global.Space.PackageId, ResourceGroups.HostedOrganizations, Global.OrganizationName, typeof(Organization)); if (item == null) { Global.ErrorMessage = string.Format("Organization {0} not found.", Global.OrganizationName); throw new Exception(Global.ErrorMessage); } Global.ItemId = item.Id; } else { action = new ImportAction(ActionTypes.ImportOrganization); action.Description = "Importing organization..."; list.Add(action); DirectoryEntry org = Global.OrgDirectoryEntry; PropertyValueCollection props = org.Properties["uPNSuffixes"]; if (props != null) { foreach (string domainName in props) { action = new ImportAction(ActionTypes.ImportOrganizationDomain); action.Description = "Importing organization domains..."; action.Name = domainName; list.Add(action); } } } if (Global.SelectedAccounts != null) { foreach (DirectoryEntry entry in Global.SelectedAccounts) { switch (entry.SchemaClassName) { case "user": action = new ImportAction(ActionTypes.ImportMailbox); action.Description = "Importing mailbox..."; action.DirectoryEntry = entry; list.Add(action); break; case "contact": action = new ImportAction(ActionTypes.ImportContact); action.Description = "Importing contact..."; action.DirectoryEntry = entry; list.Add(action); break; case "group": action = new ImportAction(ActionTypes.ImportGroup); action.Description = "Importing group..."; action.DirectoryEntry = entry; list.Add(action); break; } } } return list; } private void ImportOrganization() { PackageInfo packageInfo = Global.Space; int serviceId = PackageController.GetPackageServiceId(packageInfo.PackageId, ResourceGroups.HostedOrganizations); ServiceInfo serviceInfo = ServerController.GetServiceInfo(serviceId); StringDictionary serviceSettings = ServerController.GetServiceSettingsAdmin(serviceId); string tempDomain = serviceSettings["TempDomain"]; ServerInfo serverInfo = ServerController.GetServerById(serviceInfo.ServerId); serviceId = PackageController.GetPackageServiceId(packageInfo.PackageId, ResourceGroups.Exchange); serviceSettings = ServerController.GetServiceSettingsAdmin(serviceId); Global.MailboxCluster = serviceSettings["MailboxCluster"]; Global.StorageGroup = serviceSettings["StorageGroup"]; Global.MailboxDatabase = serviceSettings["MailboxDatabase"]; Global.KeepDeletedMailboxesDays = serviceSettings["KeepDeletedMailboxesDays"]; Global.KeepDeletedItemsDays = serviceSettings["KeepDeletedItemsDays"]; int ret = CreateOrganization(Global.Space.PackageId, Global.OrganizationId, Global.OrganizationName); if (ret > 0) { Global.ItemId = ret; } else { switch (ret) { case BusinessErrorCodes.ERROR_USER_ACCOUNT_DEMO: Global.ErrorMessage = "You cannot import organization in demo account"; break; case BusinessErrorCodes.ERROR_USER_ACCOUNT_PENDING: case BusinessErrorCodes.ERROR_USER_ACCOUNT_SUSPENDED: case BusinessErrorCodes.ERROR_USER_ACCOUNT_CANCELLED: case BusinessErrorCodes.ERROR_USER_ACCOUNT_SHOULD_BE_ADMINISTRATOR: Global.ErrorMessage = "User account is disabled or does not have enough permissions"; break; case BusinessErrorCodes.ERROR_PACKAGE_NOT_FOUND: Global.ErrorMessage = "Space not found"; break; case BusinessErrorCodes.ERROR_PACKAGE_CANCELLED: case BusinessErrorCodes.ERROR_PACKAGE_SUSPENDED: Global.ErrorMessage = "Space is cancelled or suspended"; break; case BusinessErrorCodes.ERROR_ORGS_RESOURCE_QUOTA_LIMIT: Global.ErrorMessage = "Organizations quota limit"; break; case BusinessErrorCodes.ERROR_DOMAIN_QUOTA_LIMIT: case BusinessErrorCodes.ERROR_SUBDOMAIN_QUOTA_LIMIT: Global.ErrorMessage = "Domains quota limit"; break; case BusinessErrorCodes.ERROR_ORG_ID_EXISTS: Global.ErrorMessage = "Organization with specified Id already exists"; break; case BusinessErrorCodes.ERROR_ORGANIZATION_TEMP_DOMAIN_IS_NOT_SPECIFIED: Global.ErrorMessage = "Organization temp domain name is not specified"; break; case BusinessErrorCodes.ERROR_DOMAIN_ALREADY_EXISTS: case BusinessErrorCodes.ERROR_ORGANIZATION_DOMAIN_IS_IN_USE: Global.ErrorMessage = "Domain already exists or is in use"; break; } throw new Exception(string.Format("Unable to create organization. Error code: {0}", ret)); } } public static int CreateOrganization(int packageId, string organizationId, string organizationName) { int itemId; int errorCode; // place log record Log.WriteStart(string.Format("Importing organization {0}...", organizationName)); if (!CheckQuotas(packageId, out errorCode)) return errorCode; string addressListsContainer = null; try { addressListsContainer = ADUtils.GetAddressListsContainer(); } catch (Exception ex) { Log.WriteError("Cannot load Exchange 2007 Address Lists Container", ex); return EXCHANGE_CONTAINER_ERROR; } Organization org = PopulateOrganization(packageId, organizationId, addressListsContainer); // Check if organization exitsts. if (OrganizationIdentifierExists(organizationId)) return BusinessErrorCodes.ERROR_ORG_ID_EXISTS; int serviceId = PackageController.GetPackageServiceId(packageId, ResourceGroups.HostedOrganizations); //create temprory domain name; string domainName = CreateTemporyDomainName(serviceId, organizationId); Log.WriteInfo(string.Format("Importing temp domain {0}", domainName)); if (string.IsNullOrEmpty(domainName)) return BusinessErrorCodes.ERROR_ORGANIZATION_TEMP_DOMAIN_IS_NOT_SPECIFIED; bool domainCreated; int domainId = CreateDomain(domainName, packageId, out domainCreated); //create domain if (domainId < 0) return domainId; //add organization to package items itemId = AddOrganizationToPackageItems(org, serviceId, packageId, organizationName, organizationId, domainName); // register org ID DataProvider.AddExchangeOrganization(itemId, organizationId); // register domain DataProvider.AddExchangeOrganizationDomain(itemId, domainId, true); // register organization domain service item OrganizationDomain orgDomain = new OrganizationDomain(); orgDomain.Name = domainName; orgDomain.PackageId = packageId; orgDomain.ServiceId = serviceId; PackageController.AddPackageItem(orgDomain); Log.WriteEnd("Organization imported"); return itemId; } private static Organization PopulateOrganization(int packageId, string organizationId, string addressListsContainer) { Organization org = new Organization(); org.OrganizationId = organizationId; org.AddressList = string.Format("CN={0} Address List,CN=All Address Lists,{1}", organizationId, addressListsContainer); org.GlobalAddressList = string.Format("CN={0} Global Address List,CN=All Global Address Lists,{1}", organizationId, addressListsContainer); org.OfflineAddressBook = string.Format("CN={0} Offline Address Book,CN=Offline Address Lists,{1}", organizationId, addressListsContainer); org.Database = GetDatabaseName(); org.DistinguishedName = ADUtils.RemoveADPrefix(Global.OrgDirectoryEntry.Path); org.SecurityGroup = string.Format("CN={0},{1}", organizationId, org.DistinguishedName); PackageContext cntx = PackageController.GetPackageContext(packageId); // organization limits /* org.IssueWarningKB = cntx.Quotas[Quotas.EXCHANGE2007_DISKSPACE].QuotaAllocatedValue; if (org.IssueWarningKB > 0) org.IssueWarningKB *= 1024; org.ProhibitSendKB = cntx.Quotas[Quotas.EXCHANGE2007_DISKSPACE].QuotaAllocatedValue; if (org.ProhibitSendKB > 0) org.ProhibitSendKB *= 1024; org.ProhibitSendReceiveKB = cntx.Quotas[Quotas.EXCHANGE2007_DISKSPACE].QuotaAllocatedValue; if (org.ProhibitSendReceiveKB > 0) org.ProhibitSendReceiveKB *= 1024; */ PackageSettings settings = PackageController.GetPackageSettings(packageId, PackageSettings.EXCHANGE_SERVER); org.KeepDeletedItemsDays = Utils.ParseInt(settings["KeepDeletedItemsDays"], 14); return org; } private static string GetDatabaseName() { string ret; if (String.IsNullOrEmpty(Global.MailboxCluster)) { ret = string.Format("{0}\\{1}", Global.StorageGroup, Global.MailboxDatabase); } else { ret = string.Format("{0}\\{1}\\{2}", Global.MailboxCluster, Global.StorageGroup, Global.MailboxDatabase); } return ret; } private static bool CheckQuotas(int packageId, out int errorCode) { // check account errorCode = SecurityContext.CheckAccount(DemandAccount.NotDemo | DemandAccount.IsActive); if (errorCode < 0) return false; // check package errorCode = SecurityContext.CheckPackage(packageId, DemandPackage.IsActive); if (errorCode < 0) return false; // check organizations quota QuotaValueInfo quota = PackageController.GetPackageQuota(packageId, Quotas.ORGANIZATIONS); if (quota.QuotaExhausted) { errorCode = BusinessErrorCodes.ERROR_ORGS_RESOURCE_QUOTA_LIMIT; return false; } // check sub-domains quota (for temporary domain) quota = PackageController.GetPackageQuota(packageId, Quotas.OS_SUBDOMAINS); if (quota.QuotaExhausted) { errorCode = BusinessErrorCodes.ERROR_SUBDOMAIN_QUOTA_LIMIT; return false; } return true; } private static bool OrganizationIdentifierExists(string organizationId) { return DataProvider.ExchangeOrganizationExists(organizationId); } private static string CreateTemporyDomainName(int serviceId, string organizationId) { // load service settings StringDictionary serviceSettings = ServerController.GetServiceSettingsAdmin(serviceId); string tempDomain = serviceSettings["TempDomain"]; return String.IsNullOrEmpty(tempDomain) ? null : organizationId + "." + tempDomain; } private static int CreateDomain(string domainName, int packageId, out bool domainCreated) { // trying to locate (register) temp domain DomainInfo domain = null; int domainId = 0; domainCreated = false; // check if the domain already exists int checkResult = ServerController.CheckDomain(domainName); if (checkResult == BusinessErrorCodes.ERROR_DOMAIN_ALREADY_EXISTS) { // domain exists // check if it belongs to the same space domain = ServerController.GetDomain(domainName); if (domain == null) return checkResult; if (domain.PackageId != packageId) return checkResult; if (DataProvider.ExchangeOrganizationDomainExists(domain.DomainId)) return BusinessErrorCodes.ERROR_ORGANIZATION_DOMAIN_IS_IN_USE; domainId = domain.DomainId; } else if (checkResult < 0) { return checkResult; } // create domain if required if (domain == null) { domain = CreateNewDomain(packageId, domainName); // add domain domainId = ServerController.AddDomain(domain); if (domainId < 0) return domainId; domainCreated = true; } return domainId; } private static DomainInfo CreateNewDomain(int packageId, string domainName) { // new domain DomainInfo domain = new DomainInfo(); domain.PackageId = packageId; domain.DomainName = domainName; domain.IsInstantAlias = true; domain.IsSubDomain = true; return domain; } private static int AddOrganizationToPackageItems(Organization org, int serviceId, int packageId, string organizationName, string organizationId, string domainName) { org.ServiceId = serviceId; org.PackageId = packageId; org.Name = organizationName; org.OrganizationId = organizationId; org.DefaultDomain = domainName; int itemId = PackageController.AddPackageItem(org); return itemId; } private void ImportDomain(string domainName) { int ret = -1; try { ret = AddOrganizationDomain(Global.ItemId, domainName); if (ret < 0) { Log.WriteError(string.Format("Unable to import domain {0}. Error code: {1}", domainName, ret)); Global.HasErrors = true; } } catch (Exception ex) { if (Utils.IsThreadAbortException(ex)) return; Log.WriteError(string.Format("Unable to import domain {0}. Error code: {1}", domainName, ret), ex); Global.HasErrors = true; } } private static int AddOrganizationDomain(int itemId, string domainName) { Log.WriteStart(string.Format("Importing domain {0}...", domainName)); // load organization Organization org = (Organization)PackageController.GetPackageItem(itemId); if (org == null) return -1; // check package int packageCheck = SecurityContext.CheckPackage(org.PackageId, DemandPackage.IsActive); if (packageCheck < 0) return packageCheck; DomainInfo domain = null; // check if the domain already exists int checkResult = ServerController.CheckDomain(domainName); if (checkResult == BusinessErrorCodes.ERROR_DOMAIN_ALREADY_EXISTS) { // domain exists // check if it belongs to the same space domain = ServerController.GetDomain(domainName); if (domain == null) return checkResult; if (domain.PackageId != org.PackageId) return checkResult; if (DataProvider.ExchangeOrganizationDomainExists(domain.DomainId)) return BusinessErrorCodes.ERROR_ORGANIZATION_DOMAIN_IS_IN_USE; } else if (checkResult == BusinessErrorCodes.ERROR_RESTRICTED_DOMAIN) { return checkResult; } // create domain if required if (domain == null) { domain = new DomainInfo(); domain.PackageId = org.PackageId; domain.DomainName = domainName; domain.IsInstantAlias = false; domain.IsSubDomain = false; // add domain domain.DomainId = ServerController.AddDomain(domain); } // register domain DataProvider.AddExchangeOrganizationDomain(itemId, domain.DomainId, false); // register service item OrganizationDomain exchDomain = new OrganizationDomain(); exchDomain.Name = domainName; exchDomain.PackageId = org.PackageId; exchDomain.ServiceId = org.ServiceId; PackageController.AddPackageItem(exchDomain); Log.WriteEnd("Domain imported"); return 0; } private static void ImportMailbox(DirectoryEntry directoryEntry) { int ret = -1; string name = null; try { name = (string)directoryEntry.Properties["name"].Value; ret = AddMailbox(Global.ItemId, directoryEntry); if (ret < 0) { Log.WriteError(string.Format("Unable to import mailbox {0}. Error code: {1}", name, ret)); Global.HasErrors = true; } } catch (Exception ex) { if (Utils.IsThreadAbortException(ex)) return; Log.WriteError(string.Format("Unable to import mailbox {0}. Error code: {1}", name, ret), ex); Global.HasErrors = true; } } private static void ImportContact(DirectoryEntry directoryEntry) { int ret = -1; string name = null; try { name = (string)directoryEntry.Properties["name"].Value; ret = AddContact(Global.ItemId, directoryEntry); if (ret < 0) { Log.WriteError(string.Format("Unable to import contact {0}. Error code: {1}", name, ret)); Global.HasErrors = true; } } catch (Exception ex) { if (Utils.IsThreadAbortException(ex)) return; Log.WriteError(string.Format("Unable to import contact {0}. Error code: {1}", name, ret), ex); Global.HasErrors = true; } } private static void ImportGroup(DirectoryEntry directoryEntry) { int ret = -1; string name = null; try { name = (string)directoryEntry.Properties["name"].Value; ret = AddGroup(Global.ItemId, directoryEntry); if (ret < 0) { Log.WriteError(string.Format("Unable to import group {0}. Error code: {1}", name, ret)); Global.HasErrors = true; } } catch (Exception ex) { if (Utils.IsThreadAbortException(ex)) return; Log.WriteError(string.Format("Unable to import group {0}. Error code: {1}", name, ret), ex); Global.HasErrors = true; } } private static int AddMailbox(int itemId, DirectoryEntry entry) { string accountName = (string)entry.Properties["name"].Value; Log.WriteStart(string.Format("Importing user {0}...", accountName)); Organization org = (Organization)PackageController.GetPackageItem(itemId); if (org == null) return -1; // e-mail string email = (string)entry.Properties["userPrincipalName"].Value; if (string.IsNullOrEmpty(email)) throw new Exception("UPN is not specified"); if (EmailAddressExists(email)) return BusinessErrorCodes.ERROR_EXCHANGE_EMAIL_EXISTS; if (AccountExists(accountName)) throw new Exception(string.Format("Account {0} already exists", accountName)); string displayName = (string)entry.Properties["displayName"].Value; string samName = (string)entry.Properties["sAMAccountName"].Value; // this should really NEVER happen - an AD account without sAMAccountName?! if (string.IsNullOrEmpty(samName)) throw new Exception("SAMAccountName is not specified"); // add Netbios-Domainname before samAccountName - format in the database samName = Global.NetBiosDomain + "\\" + samName; int userId = AddOrganizationUser(itemId, accountName, displayName, email, samName, string.Empty); AddAccountEmailAddress(userId, email); //account type PropertyValueCollection type = entry.Properties["msExchRecipientDisplayType"]; if (type == null || type.Value == null) { Log.WriteInfo("Account type : user"); return userId; } int mailboxType = (int)type.Value; int mailboxTypeDetails = 0; PropertyValueCollection typeDetails = entry.Properties["msExchRecipientTypeDetails"]; if (typeDetails!=null) { if (typeDetails.Value != null) { try { object adsLargeInteger = typeDetails.Value; mailboxTypeDetails = (Int32)adsLargeInteger.GetType().InvokeMember("LowPart", System.Reflection.BindingFlags.GetProperty, null, adsLargeInteger, null); } catch { } // just skip } } ExchangeAccountType accountType = ExchangeAccountType.Undefined; if (mailboxTypeDetails == 4) { Log.WriteInfo("Account type : shared mailbox"); accountType = ExchangeAccountType.SharedMailbox; } else { switch (mailboxType) { case 1073741824: Log.WriteInfo("Account type : mailbox"); accountType = ExchangeAccountType.Mailbox; break; case 7: Log.WriteInfo("Account type : room"); accountType = ExchangeAccountType.Room; break; case 8: Log.WriteInfo("Account type : equipment"); accountType = ExchangeAccountType.Equipment; break; default: Log.WriteInfo("Account type : unknown"); return userId; } } UpdateExchangeAccount(userId, accountName, accountType, displayName, email, false, string.Empty, samName, string.Empty, Global.defaultMailboxPlanId); string defaultEmail = (string)entry.Properties["extensionAttribute3"].Value; PropertyValueCollection emails = entry.Properties["proxyAddresses"]; if (emails != null) { foreach (string mail in emails) { string emailAddress = mail; if (emailAddress.ToLower().StartsWith("smtp:")) emailAddress = emailAddress.Substring(5); if (EmailAddressExists(emailAddress)) { if ((!emailAddress.Equals(defaultEmail, StringComparison.InvariantCultureIgnoreCase)) && (!emailAddress.Equals(email, StringComparison.InvariantCultureIgnoreCase))) Log.WriteInfo(string.Format("Email address {0} already exists. Skipped", emailAddress)); continue; } // register email address Log.WriteInfo(string.Format("Importing email {0}", emailAddress)); AddAccountEmailAddress(userId, emailAddress); } } Log.WriteEnd("User imported"); return userId; } private static int AddContact(int itemId, DirectoryEntry entry) { string accountName = (string)entry.Properties["name"].Value; Log.WriteStart(string.Format("Importing contact {0}...", accountName)); Organization org = (Organization)PackageController.GetPackageItem(itemId); if (org == null) return -1; if (AccountExists(accountName)) throw new Exception(string.Format("Account {0} already exists", accountName)); string displayName = (string)entry.Properties["displayName"].Value; string email = (string)entry.Properties["targetAddress"].Value; if (email != null && email.ToLower().StartsWith("smtp:")) email = email.Substring(5); // no sAMAccountName for contacts - so String.Empty is OK int accountId = AddAccount(itemId, ExchangeAccountType.Contact, accountName, displayName, email, false, 0, string.Empty, null); Log.WriteEnd("Contact imported"); return accountId; } private static int AddGroup(int itemId, DirectoryEntry entry) { string accountName = (string)entry.Properties["name"].Value; Log.WriteStart(string.Format("Importing group {0}...", accountName)); Organization org = (Organization)PackageController.GetPackageItem(itemId); if (org == null) return -1; if (AccountExists(accountName)) throw new Exception(string.Format("Account {0} already exists", accountName)); string displayName = (string)entry.Properties["displayName"].Value; string email = null; PropertyValueCollection proxyAddresses = entry.Properties["proxyAddresses"]; if (proxyAddresses != null) { foreach (string address in proxyAddresses) { if (address != null && address.StartsWith("SMTP:")) { email = address.Substring(5); break; } } } if (string.IsNullOrEmpty(email)) throw new Exception("Email is not specified"); if (EmailAddressExists(email)) return BusinessErrorCodes.ERROR_EXCHANGE_EMAIL_EXISTS; string samName = (string)entry.Properties["sAMAccountName"].Value; // this should really NEVER happen - an AD group without sAMAccountName?! if (string.IsNullOrEmpty(samName)) throw new Exception("SAMAccountName is not specified"); // add Netbios-Domainname before samAccountName - format in the database samName = Global.NetBiosDomain + "\\" + samName; int accountId = AddAccount(itemId, ExchangeAccountType.DistributionList, accountName, displayName, email, false, 0, samName, null); AddAccountEmailAddress(accountId, email); string defaultEmail = (string)entry.Properties["extensionAttribute3"].Value; PropertyValueCollection emails = entry.Properties["proxyAddresses"]; if (emails != null) { foreach (string mail in emails) { string emailAddress = mail; if (emailAddress.ToLower().StartsWith("smtp:")) emailAddress = emailAddress.Substring(5); if (!emailAddress.Equals(defaultEmail, StringComparison.InvariantCultureIgnoreCase)) { if (EmailAddressExists(emailAddress)) { Log.WriteInfo(string.Format("Email address {0} already exists. Skipped", emailAddress)); continue; } // register email address Log.WriteInfo(string.Format("Importing email {0}", emailAddress)); AddAccountEmailAddress(accountId, emailAddress); } } } Log.WriteEnd("Group imported"); return accountId; } private static bool EmailAddressExists(string emailAddress) { return DataProvider.ExchangeAccountEmailAddressExists(emailAddress); } private static bool AccountExists(string accountName) { return DataProvider.ExchangeAccountExists(accountName); } private static int AddAccount(int itemId, ExchangeAccountType accountType, string accountName, string displayName, string primaryEmailAddress, bool mailEnabledPublicFolder, MailboxManagerActions mailboxManagerActions, string samAccountName, string accountPassword) { return DataProvider.AddExchangeAccount(itemId, (int)accountType, accountName, displayName, primaryEmailAddress, mailEnabledPublicFolder, mailboxManagerActions.ToString(), samAccountName, CryptoUtils.Encrypt(accountPassword),0, string.Empty); } private static int AddOrganizationUser(int itemId, string accountName, string displayName, string email, string samAccountName, string accountPassword) { return DataProvider.AddExchangeAccount(itemId, (int)ExchangeAccountType.User, accountName, displayName, email, false, string.Empty, samAccountName, CryptoUtils.Encrypt(accountPassword), 0 , string.Empty); } private static void AddAccountEmailAddress(int accountId, string emailAddress) { DataProvider.AddExchangeAccountEmailAddress(accountId, emailAddress); } private static void UpdateExchangeAccount(int accountId, string accountName, ExchangeAccountType accountType, string displayName, string primaryEmailAddress, bool mailEnabledPublicFolder, string mailboxManagerActions, string samAccountName, string accountPassword, int mailboxPlanId) { DataProvider.UpdateExchangeAccount(accountId, accountName, accountType, displayName, primaryEmailAddress, mailEnabledPublicFolder, mailboxManagerActions, samAccountName, CryptoUtils.Encrypt(accountPassword), mailboxPlanId , -1, string.Empty, false); } } }