Скрипт для массового изменения учетных записей электронной почты в Outlook Express и Microsoft Outlook (часть 2)

В первой части речь шла о массовой замене имен/ip-адресов smtp и imap серверов в учетных записях Outlook Express. Ну, а здесь повествование пойдет о том, как проделать аналогичную операцию с учетными записями MS Outlook. Для начала потребовалось найти в реестре раздел, в котором MS Outlook хранит настройки учетных записей. Выяснилось, что структура данных MS Outlook, хранящихся в реестре, сложнее, чем в случае с Outlook Express: в разделе HKCU\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\ содержится информация о профилях (конфигурациях) MS Outlook. Каждый подраздел этого раздела реестра имеет то же самое имя, что и название профиля MS Outlook. Так, например, если в MS Ootlook у нас создано 2 профиля Outlook и Test,

  

то, соответствующий раздел реестра будет выглядеть следующим образом:

В каждом профиле MS Outlook могут быть созданы учетные записи электронной почты, информация о которых будет размещена в реестре по следующему пути: HKCU\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\<имя_профиля>\9375CFF0413111d3B88A00104B2A6676

 

Для каждой учетной записи будет создан, как и в случае с Outlook Express, свой «номерной» подраздел. При создании пользователем очередной учетной записи электронной почты MS Outlook, в реестре будет создан очередной «последний номерной + 1» раздел, в котором будут храниться параметры этой учетной записи. (см. Information about the location of the registry entries for signature data files and for the vCard file in Outlook 2003 and in Outlook 2002)

Таким образом, получается, что наша задача состоит в том, чтобы

  • перебрать все профили MS Outlook,
  • внутри каждого профиля перебрать все учетные записи
  • в каждой учетной записи найти параметры, хранящие информацию об imap и smtp серверах, и в том случае, если этот параметр реестра содержит старое имя сервера, заменить его новым.

 

Вот, что у меня получилось в результате:

//////////////////////////////////////////////////////////////////////////////////
// shs 20071114
// JScript ChangeOutlookSMTPIMAPservers.js
// Замена текущих значений SMTP и IMAP серверов в учетных записях Outlook
//
// Пример вызова: cscript ChangeOutlookSMTPIMAPservers.js <old_SMTP_server> <new_SMTP_server> [<old_LDAP_server> <new_LDAP_server>]
//////////////////////////////////////////////////////////////////////////////////
var WshShell = WScript.CreateObject ("WScript.Shell");
var SubKeys;
var arrProfiles			= new Array ();
var arrProfileSubkeys	= new Array();
var strMailProfiles = "HKCU\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows Messaging Subsystem\\Profiles";
// Подключ реестра, хранящий информацию об учетных записях электронной почты
//"Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows Messaging Subsystem\\Profiles\\<имя почтового профиля>\\9375CFF0413111d3B88A00104B2A6676";
var strMailAccountsSubKey = "9375CFF0413111d3B88A00104B2A6676";
var strComputer = ".";
//
if ((WScript.Arguments.Count()!=4|WScript.Arguments.Count()!=6) &amp; false) //false - заглушка
{
  WScript.Echo ("Ошибка! Неправильное количество аргументов. \n"+
  "Пример вызова: cscript ChangeOutlookSMTPIMAPservers.js <old_SMTP_server> <new_SMTP_server> <old_IMAP_server> <new_IMAP_server> [<old_LDAP_server> <new_LDAP_server>]");
}
else
{
  //Заглушка (пока работаем без параметров командной строки)
  var arrTypeOfServer = new Array ("SMTP Server", "IMAP Server");
  var arrOldServer = new Array();
  var arrNewServer = new Array();
  arrOldServer[0] = "oldmailserver";   // WScript.Arguments.Item(0); //старый SMTP-сервер
  arrOldServer[1] = "oldmailserver";  // WScript.Arguments.Item(2);  //старый IMAP-сервер
  arrNewServer[0] = "newmailserver";   // WScript.Arguments.Item(1); //новый SMTP-сервер
  arrNewServer[1] = "newmailserver";  // WScript.Arguments.Item(3);  //новый IMAP-сервер
  var strOldLDAPServer, strNewLDAPServer;
  strOldLDAP=strNewLDAP="";
  strOldLDAPServer  = "ldap";  // WScript.Arguments.Item(4); //старый LDAP-сервер
  strNewLDAPServer  = "ldap";   // WScript.Arguments.Item(5); //новый LDAP-сервер
  //Получаем массив почтовых профилей
  arrProfiles = EnumSubKeys(strComputer, strMailProfiles);
  for (var iProfNdx=0;iProfNdx<arrProfiles.length;iProfNdx++)
  {
      //Перечисляем все подключи почтового профиля
      //В каждом из этих подключей может содержаться информация об Адресной книге
      arrProfileSubkeys = EnumSubKeys(strComputer, strMailProfiles+"\\"+arrProfiles[iProfNdx]);
      for (var iPSndx=0;iPSndx<arrProfileSubkeys.length;iPSndx++)
      {
      	//Учетные записи электронной почты храняться в особом подключе (разделе) почтового профиля strMailAccountsSubKey = "9375CFF0413111d3B88A00104B2A6676"
      	if (arrProfileSubkeys[iPSndx]==strMailAccountsSubKey)
   		{
      		//Для каждого почтового профиля выполняем перечисление учетных записей электронной почты
      		arrAccounts = EnumSubKeys(strComputer, strMailProfiles+"\\"+arrProfiles[iProfNdx]+"\\"+strMailAccountsSubKey);
      		for (var iAccNdx=0;iAccNdx<arrAccounts.length;iAccNdx++)
      		{
      			for (var iTOSNdx in arrTypeOfServer)
      			{
      				var vararrServer=""
      				//в каждой учетной записи пытаемся прочитать значение параметра, содержащееся в массиве arrTypeOfServer
      				try
      				{
      					vararrServer = WshShell.RegRead(strMailProfiles+"\\"+arrProfiles[iProfNdx]+"\\"+strMailAccountsSubKey+"\\"+arrAccounts[iAccNdx]+"\\"+arrTypeOfServer[iTOSNdx]);
      				}
      				catch(e){}
      				if (vararrServer!="")
      				{

      					if (arrOldServer[iTOSNdx]==Array2String(vararrServer.toArray()))
      					{
      					  WScript.Echo(strMailProfiles+"\\"+arrProfiles[iProfNdx]+"\\"+strMailAccountsSubKey+"\\"+arrAccounts[iAccNdx]+"\\"+arrTypeOfServer[iTOSNdx]);
      					  WScript.Echo("Found: " +arrTypeOfServer[iTOSNdx]+" = "+Array2String(vararrServer.toArray())+"...");
        					//Заменяем старое название сервера на новое
        					WriteRegBin(strComputer, strMailProfiles+"\\"+arrProfiles[iProfNdx]+"\\"+strMailAccountsSubKey+"\\"+arrAccounts[iAccNdx]+"\\", arrTypeOfServer[iTOSNdx], toVBArray(Str2Arr(arrNewServer[iTOSNdx])));
        					WScript.Echo("... and changed to " +arrTypeOfServer[iTOSNdx]+" = "+arrNewServer[iTOSNdx]+"\n");
      					}
      				}
      			}
      		}//end for
      	}//end if
      }//end for
  }
}
//------------------------------------------------------------------------------
//Эта функция выполняет перечисление подключей реестра
//(для заданного компьютера и заданного ключа реестра)
//Приходится использовать WMI, т.к. WSH не имеет средств для выполнения данной задачи
function EnumSubKeys (strComputer,RegKey)
{
  var RootKey = new Object()
  RootKey["HKCR"] = RootKey["HKEY_CLASSES_ROOT"]   = 0x80000000;
  RootKey["HKCU"] = RootKey["HKEY_CURRENT_USER"]   = 0x80000001;
  RootKey["HKLM"] = RootKey["HKEY_LOCAL_MACHINE"]  = 0x80000002;
  RootKey["HKUS"] = RootKey["HKEY_USERS"]          = 0x80000003;
  RootKey["HKCC"] = RootKey["HKEY_CURRENT_CONFIG"] = 0x80000005;
  var RootVal = RootKey[RegKey.substr(0, RegKey.indexOf("\\"))]
  if (RootVal != undefined)
  {
  	var oReg=GetObject("winmgmts:{impersonationLevel=impersonate}!\\\\" + strComputer + "\\root\\default:StdRegProv");
  	var method = oReg.Methods_.Item("EnumKey");
  	var inParams = method.InParameters.SpawnInstance_();
	inParams.hDefKey = RootVal;
	inParams.sSubKeyName = RegKey.substr(RegKey.indexOf("\\") + 1);
	var outParams = oReg.ExecMethod_(method.Name, inParams);
	return outParams.sNames.toArray();
  }
}
//------------------------------------------------------------------------------
//Функция для преобразования массива байт в строку
//массив байт имеет следующий вид: (x,0,y,0,...,0,0) (где x,y - коды символов)
function Array2String (arrBytes)
{
	var strResult="";
	//var String=new Object();
    for (Index in arrBytes)
    {
    	if (Index%2==0)
    	{
    		strResult=strResult+((arrBytes[Index]==0)?"":String.fromCharCode(arrBytes[Index]));
    	}
    }
    return strResult;
}
//------------------------------------------------------------------------------
//Функция для записи в реестр параметра типа REG_BINARY
//Приходится использовать WMI, т.к. метод WSH RegWrite позволяет записать в реестр
//значение, величина которого ограничена сверху величиной max integer
//
function WriteRegBin (strComputer,RegKey, sValueName, vbarrValue)
{
  var RootKey = new Object()
  RootKey["HKCR"] = RootKey["HKEY_CLASSES_ROOT"]   = 0x80000000;
  RootKey["HKCU"] = RootKey["HKEY_CURRENT_USER"]   = 0x80000001;
  RootKey["HKLM"] = RootKey["HKEY_LOCAL_MACHINE"]  = 0x80000002;
  RootKey["HKUS"] = RootKey["HKEY_USERS"]          = 0x80000003;
  RootKey["HKCC"] = RootKey["HKEY_CURRENT_CONFIG"] = 0x80000005;
  var RootVal = RootKey[RegKey.substr(0, RegKey.indexOf("\\"))]
  if (RootVal != undefined)
  {
 	var oReg=GetObject("winmgmts:{impersonationLevel=impersonate}!\\\\" + strComputer + "\\root\\default:StdRegProv");
 	var method = oReg.Methods_.Item("SetBinaryValue");
 	var inParams = method.InParameters.SpawnInstance_();
	inParams.hDefKey = RootVal;
	inParams.sSubKeyName = RegKey.substr(RegKey.indexOf("\\") + 1);
	inParams.sValueName = sValueName;
	inParams.uValue = vbarrValue;
	var outParams = oReg.ExecMethod_(method.Name, inParams);
	return outParams.ReturnValue; //Код возврата, если = 0, значит все OK
  }
}
//------------------------------------------------------------------------------
//Фунция для перобразования строки в массив байт
//массив байт имеет следующий вид: (x,0,y,0,...,0,0) (где x,y - коды символов)
function Str2Arr(strText)
{
var arrReturn = new Array();

for (var iCharNdx=0;iCharNdx<strText.length;iCharNdx++)
  {
    arrReturn[iCharNdx*2]= strText.charCodeAt(iCharNdx);
    arrReturn[iCharNdx*2+1]= 0;
  }
  arrReturn[strText.length*2]= 0;
  arrReturn[strText.length*2+1]= 0;
  return arrReturn;
}
//------------------------------------------------------------------------------
//функция для преобразования массива JScript в safearray:
// METHOD toVBArray()
// Returns a safearray containing the items in the specified JScript array.
// The JScript array must be zero-based and have contiguous elements.
function toVBArray(JSArray)
{
var i;
var g_dict = new ActiveXObject("Scripting.Dictionary");

g_dict.RemoveAll();

for (i = 0; i < JSArray.length; i++)
g_dict.Add(i, JSArray[i]);

return g_dict.Items();
}

Небольшие пояснения по скрипту.

Для перечисления подразделов реестра пришлось использовать WMI, т.к. WSH не имеет средств выполнения этой простой задачки. К использованию WMI пришлось прибегнуть так же и в случае записи в реестр значения типа REG_BINARY, т.к. оказалось, что метод RegWrite объекта WScript.Shell может записывать в реестр только небольшие числа этого типа (не больше, чем MAX Integer). Как ни странно, но параметры реестра, хранящие имена серверов IMAP и SMTP, имеют тип именно REG_BINARY и могут содержать значения много большие, чем MAX Integer.

Использовать некоторые методы объектов WMI в Jscript, вызывая их традиционным способом Object.Method, не всегда возможно, т.к.  некоторые методы объектов WMI возвращают значения через параметры вызова, а JScript не поддерживает возврат значений через параметры вызова функции. Поэтому приходится идти обходными путями, о которых я упоминал в первой части: сначала получаем «объект-метод»

var method = oReg.Methods_.Item("SetBinaryValue");

затем формируем объект, чьи поля содержат входные параметрами вызова этого метода
           

	var inParams = method.InParameters.SpawnInstance_();
	inParams.hDefKey = RootVal;
	inParams.sSubKeyName = RegKey.substr(RegKey.indexOf("\\") + 1);
	inParams.sValueName = sValueName;
	inParams.uValue = vbarrValue;

вызываем функцию исполнения метода, для которого мы определили входные параметры

var outParams = oReg.ExecMethod_(method.Name, inParams);

и, наконец, производим разбор (выбор нужных) выходных параметров, которые мы получаем, как результат работы функции вызова нашего метода

return outParams.ReturnValue;

19 Comments

  1. Pingback: Скрипт для массового изменения учетных записей электронной почты в Outlook Express и Microsoft Outlook. « ShS's Blog

    • > Однако я использую для сервисов fqdn, отличные от fqdn серверов (через CNAME в dns прописаны). типа mail.services.novgaro.ru. И при смене ip или сервера исправляю только cname в DNS.

      Здесь не шла речь о смене IP, тут проблема была в другом: пользователь был смигрирован из домена в домен. У него в профиле остались старые настройки учетной записи эл. почты. А нужно прописать новые, т.к. в новом домене – у нас новый почтовый сервер.
      Изменять что-либо в DNS – не вариант. Т.к., если в почтовой учетке был FQDN, то теперь он ссылается на старый домен (ставший чужим), а значит надо править учетную запись почтового клиента. Если в учетке было короткое имя, то, конечно, можно внести такой же CNAME в зоне нового домена, чтобы по короткому имени мы попадали на новый сервер. Но… тут, помимо порядка суффиксов поиска DNS, свое влияние может оказывать и nebios (если при помощи DNS по каким-то приичинам не удалось разрешить имя). В общем, чтобы гарантировать, что смигрированный пользователь будет обращаться на новый почтовый сервер, я и сделал этот скрипт.

  2. А можно ли переделать скрипт дабы во всех профилях устанавливался ключ ключ Leave on Server в шестнацетеричное значение a0006
    О целесообразности: Тк груповых политик по этому вопросу нет данный скрипт позволит поменять через GPO всем данный ключ если организации не хочется чтобы пользователи хранили письма на сервере
    Вообщебы хорошо написать чтото более уневерсальное по смене параметров в профилях может создание профилей по данным пользователей системы хотя это уже будет титанический труд

    • Этим скриптом можно заменять любой параметр реестра, который находится в одном из подразделов реестра, хранящем параметры профиля. Если вы хотите поменять именно такой параметр реестра, то вам нужно добавить соответствующие значения к массивам arrOldServer, arrNewServer и arrTypeOfServer.

  3. День добрый.
    Я так понял, скрипт должен был и настройки адресной книги LDAP менять, но этот функционал не дописан?
    Эти настройки хранятся в другой подветке: HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\Outlook\a1655fec5a11f64ca9057cba78b59ea5.

  4. Добрый день!
    А подскажите пожалуйста, как можно создать новую учетную запись в почтовом профиле.
    Спасибо

    • Не знаю/не помню. Но по любому возможен один из 2х вариантов: путем непосредственного внесения изменний в реестр или при помощи prf-файлов.

  5. Задача – на всех ПК во всех доменных учётных записях найти все учётные записи Outlook Express и заменить: SMTP Server, POP3 Server, POP3 User Name.
    Если для замены серверов всё работает и понятно, т.к. используются явные “ручные” записи адресов в массиве, то как “POP3 User Name” заменить на часть адреса до @ параметра “SMTP Email Address” – непонятно.
    Не сможет никто подсказать?

  6. Спасибо.
    Не подскажите как добавить порт для smtp после переименования? До этого использовался 25, а т.к. это порт по умолчанию про него нет записи в реестре, нужно добавить новый параметр.
    “SMTP Port”=dword:000001d1

  7. Спасибо, что ответили, но добыть я как раз тоже пытался может не правильно делал, но как то хитро меняются порты что толком ни чего не нашел…

Leave a Reply

Your email address will not be published. Required fields are marked *

Notify me of followup comments via e-mail. You can also subscribe without commenting.